React
update 구현 : Update 구현
update는 read와 create 기능이 결합된 것으로 볼 수 있다.
폼이 있어야 수정할 수 있고 기존 컨텐츠를 불러와야 한다.
그래서 form이 이미 구현된 createContent.js 컴포넌트를 복사해준다.
CreateContent 를 UpdateContent로 바꿔주고 App.js에도 사용할 수 있도록 넣어준다.
import UpdateContent from './components/UpdateContent';
그리고 mode 가 update가 되면 실행되도록 if .. if else문의 if else를 복사해서 하나 더 만들어준다.
지금 render 부분이 너무 혼란스럽기 때문에 새로운 함수를 만들어 쪼갠다.
getContent(){
var _title, _desc, _article = null;
if(this.state.mode === 'welcome'){
_title = this.state.welcome.title;
_desc = this.state.welcome.desc;
_article = < ReadContent title={_title} desc={_desc}>< /ReadContent>
}else if(this.state.mode === 'read'){
var i = 0;
while(i < this.state.contents.length){
var data = this.state.contents[i];
if(data.id === this.state.selected_content_id){
_title = data.title;
_desc = data.desc;
break;
}
i = i+1;
}
_article = < ReadContent title={_title} desc={_desc}>< /ReadContent>
}else if(this.state.mode === 'create'){
_article = < CreateContent onSubmit={function(_title, _desc){
//인자로 title과 desc가 전달될 수 있다면
//setState를 통해서 새로운 content 값을 추가
//this.state.contents
//id 값 1 증가시키기
this.max_content_id = this.max_content_id+1;
// this.state.contents.push(
// {id:this.max_content_id, title:_title, desc:_desc}
// ); 기존 컨텐츠 배열에 값을 하나 추가하는 것
// var _contents = this.state.contents.concat(
// {id:this.max_content_id, title:_title, desc:_desc}
// )
//Array.from 쓰기
var newContents = Array.from(this.state.contents);
newContents.push({id:this.max_content_id, title:_title, desc:_desc});
this.setState({
contents: newContents
});
//this.setState({
// contents: _contents
//});
}.bind(this)}>< /CreateContent>
}else if(this.state.mode === 'update'){
_article = < UpdateContent onSubmit={function(_title, _desc){
this.max_content_id = this.max_content_id+1;
var newContents = Array.from(this.state.contents);
newContents.push({id:this.max_content_id, title:_title, desc:_desc});
this.setState({
contents: newContents
});
}.bind(this)}>< /UpdateContent>
}
return _article;
}
그리고 아래 Control 컴포넌트에 _article 대신 this.getContent() 를 넣어준다.
그 다음 UpdateContent가 실행될 때
입력값으로 현재 선택된 content id에 따른 값을 UpdateContent에 넣어줘야 한다.
contents에서 selected_content_id와 같은 원소를 찾아줘야 한다.
read에서 썼던 것 처럼 while문으로 찾아준다.
getContent() 위에 getReadContent()라는 새로운 함수를 만들고
아래에서 사용한 변수를 넣어준다.
getReadContent(){
var i = 0;
while(i < this.state.contents.length){
var data = this.state.contents[i];
if(data.id === this.state.selected_content_id){
_title = data.title;
_desc = data.desc;
break;
}
i = i+1;
}
그리고 _title과 _desc부분을 data 로 바꿔주고
getReadContent(){
var i = 0;
while(i < this.state.contents.length){
var data = this.state.contents[i];
if(data.id === this.state.selected_content_id){
return data;
break;
}
i = i+1;
}
아래의 while문을 getReadContent();함수로 바꿔준다.
else if(this.state.mode === 'read'){
var _content = this.getReadContent();
_article = < ReadContent title={_content.title} desc={_content.desc}>< /ReadContent>
}
그리고 아까 만들어 둔 else if update 부분에도
_content = this.getReadContent(); 을 넣어준 후, UpdateContent에 data로 넣어준다.
else if(this.state.mode === 'update'){
_content = this.getReadContent();
_article = < UpdateContent data={_content} onSubmit={function(_title, _desc){
this.max_content_id = this.max_content_id+1;
var newContents = Array.from(this.state.contents);
newContents.push({id:this.max_content_id, title:_title, desc:_desc});
this.setState({
contents: newContents
});
}.bind(this)}>< /UpdateContent>
}
그리고 UpdateContent에서 console로 찍어보면 console에 해당 데이터가 잘 들어오는 것을 알 수 있다.
update 구현 : form
컴포넌트로 주입된 data를 기반으로 컴포넌트에 기본적으로 입력되는 값을 세팅하기
반드시 참고해야 하는
React.js
UpdateContent.js 파일의 input 부분을 이렇게 줄바꿈 한다.
< p>
< input
type="text"
name="title"
placeholder="title"
></>
< /p>
< p>
< textarea
name="desc"
placeholder="description"
>< /textarea>
< /p>
< p>
< input
type="submit"
>< /input>
< /p>
그리고 input title 부분에 value={this.props.data.title}를 넣어주면
다시 새로고침 했을 때 title 부분에 지금 지정된 id 값의 title이 들어온다.
그러나 수정은 안된다.
왜냐면 this.props.data.title에서 가지고 온 데이터는 props에서 가지고 온 것이기 때문에
수정할 수 없는 readonly 상태가 된다.
그러면 컴포넌트 내에서 value에 넣을 값을 state화 시켜준다.
constructor(props){
super(props);
this.state = {
title:this.props.data.title
}
}
props 를 생성자로 생성하고
이제 this.props.data.title의 this.props.data를 this.state로 바꿔준다.
하지만 아직 props가 state가 되었다고 해서 수정이 가능해질 근거가 없다.
input의 값을 바꾸었을 때 state의 값이 바뀌어야만 readonly가 풀린 것으로 본다.
그러면 input에 onChange 이벤트를 넣어준다.
< p>
< input
type="text"
name="title"
placeholder="title"
value={this.state.title}
onChange={function(e){
console.log(e.target.value);
}.bind(this)}
>< /input>
< /p>
이렇게 되면 한 글자씩 변할때마다 콘솔에 출력된다.
이제 여기에 this.setState({title:e.target.value}); 를 넣어 주면 수정이 된다.
< pre>
< input
type="text"
name="title"
placeholder="title"
value={this.state.title}
onChange={function(e){
console.log(e.target.value);
this.setState({title:e.target.value});
}.bind(this)}
>< /input>
< /p>
</pre>
그럼 이제 textarea도 setState를 넣어줘야 한다.
props에 title을 만들어 두었던 곳 아래에 desc도 만들어 준다.
constructor(props){
super(props);
this.state = {
title:this.props.data.title,
desc:this.props.data.desc
}
}
그리고 textarea도 다음과 같이 바꾸고 onChange이벤트도 그대로 가지고 와서 title을 desc로 바꾼다.
< p>
< textarea
onChange={function(e){
console.log(e.target.value);
this.setState({desc:e.target.value});
}.bind(this)}
name="desc"
placeholder="description"
value={this.state.desc}
> < /textarea>
< /p>
하지만 이렇가 하나하나 onChange를 만드는 것은 귀찮기 때문에 inputFormHandler(){}라는 이름의 함수를 하나 만들어서 중복을 제거한다.
inputFormHandler(e){
this.setState({title:e.target.value});
}
그리고 input의 onChange를 바꾼다.
< p>
< input
type="text"
name="title"
placeholder="title"
value={this.state.title}
onChange={this.inputFormHandler.bind(this)}
>< /input>
< /p>
하지만 이 경우에는 setState에 title이라고 되어 있기 때문에 아래 desc 에서는 사용할 수 없으므로 지금 이벤트가 발생하고 있는 태그의 name을 알아내는 방법을 쓴다. title을 지우고 `e.target.name`을 사용해 다음과 같이 만든다.
inputFormHandler(e){
this.setState({[e.target.name]:e.target.value});
//대괄호 사용
}
이렇게 하면 title 부분에 target의 name이 대신 들어오게 된다.
그리고 construnctor에다가 bind(this)를 가지고 있을 수 있게 만든다.
constructor(props){
super(props);
this.state = {
title:this.props.data.title,
desc:this.props.data.desc
}
this.inputFormHandler = this.inputFormHandler.bind(this);
}
그러면 이제 아래에 일일이 bind(this)를 가지고 있을 필요가 없다.
###### update 구현 : state 변경 이전시간까지 props로 들어온 데이터를 state로 만들고 state의 값을 각각의 폼과 동기화 시키는 방법을 구현했다. 하지만 어디에 대한 부분을 업데이트 할 것인지 식별자 부분이 필요하다. 폼에서는 사용자에게 보일 필요가 없는 부분에 hidden을 쓴다.
< input type="hidden" name="id" value={this.state.id}>< /input>
form 안에 hidden을 넣어준다.
변경이 없기 때문에 onChange 는 들어갈 필요 없다.
그리고 onSubmit 이벤트 발생 시 id 값을 넣어준다.
< form action="/create_process" method="post"
onSubmit={function(e){
e.preventDefault();
this.props.onSubmit(
this.state.id,
this.state.title,
this.state.desc
);
}.bind(this)}
>
그리고 App.js의 Update 부분을 수정한다.
onSubmit이 실행될 때 첫번째 인자로 id값을 주는 것으로 바꾼다.
else if(this.state.mode === 'update'){
_content = this.getReadContent();
_article = < UpdateContent data={_content} onSubmit={
function(_id, _title, _desc){
this.max_content_id = this.max_content_id+1;
var _contents = this.state.contents.concat(
{id:this.max_content_id, title:_title, desc:_desc}
);
this.setState({
contents: _contents
});
}.bind(this)}>< /UpdateContent>
}
그리고 max_content_id는 create를 할 때 필요한 것이기 때문에 삭제한다.
concat은 기존 데이터를 추가할 때 썼던 것인데 지금은 수정하려고 한다.
그러기 위해 변수 _contents를 복제한다.
**Array.from**를 사용한다.
`Array.from(this.state.contents);` 하면 this.state의 contents 배열이 복사되어 새로 만들어진다. 이것을 _contents라는 변수에 담아준다. 그 다음이 _contents에 담긴 값 중에서 우리가 수정해야 하는 값을 찾는다.
var i = 0;
while(i < contents.length){
if(_contents[i].id === _id){
_contents[i] = {id:_id, title:_title, desc:_desc}
break;
}
i = i + 1;
}
기존 원본을 수정하지 않고 immutable 테크닉으로 기존의 것을 복제해서 사용해야 한다.
수정 후 바로 내용을 보기 위해 mode를 read로 바꿔준다.
###### update 구현 : delete 구현 mode를 welcome으로 바꿔둔다.
delete 버튼은 Control 컴포넌트 안에 있다. delete 는 onChangeMode라는 props를 호출하고 있다. _mode 라는 값이 delete로 호출되면 삭제 오퍼레이션이 시작된 것이다. App.js의 Control 부분에 조건문으로 mode가 delete인 경우 삭제 오퍼레이션을 진행할 수 있게 조건문을 넣어준다.
< Control onChangeMode={function(_mode){
if(_mode === 'delete'){
//정말 삭제?
if(window.confirm()){
}
} else{
this.setState({
mode:_mode
})
}
}.bind(this)}>< /Control>
`window.confirm()` 은 `alert()` 와 다르게 window가 붙어야 한다.
이 confirm을 실행했을 때, 사용자가 확인을 누르면 delete가 실행되도록 한다.
누구를 삭제할 것인가는 State의 selected_content_id를 통해 알 수 있다.
어떤..데이터...?(잘 안들리는 2:53) 를 삭제할 것인가는 contents라고 되어있는 부분에서 찾아야 한다.
while 문을 사용한다. 이 떄 삭제는 **splice()** 를 사용한다.
< Control onChangeMode={function(_mode){
if(_mode === 'delete'){
//정말 삭제?
if(window.confirm('really??')){
var _contents = Array.from(this.state.contents);
var i = 0;
while(i < _contents.length){
if(_contents[i].id === this.state.selected_content_id){
_contents.splice(i, 1);
break;
}
i = i + 1;
}
this.setState({
mode:'welcome',
contents:_contents
});
alert('deleted!');
}
} else{
this.setState({
mode:_mode
})
}
}.bind(this)}>< /Control>
이럼 삭제 완료!
###### 수업을 마치며 **immutable 라이브러리**
배열, 객체의 대체재로 사용할 수 있다. 복제된 원본이 수정된 결과를 리턴한다.
리액트와 단짝이다.
**react router**
url에 따라 적당한 컴포넌트를 사용할 수 있도록 한다. 플러그인과 같은 기능으로 permalink 기능도 제공한다. **create-reate-app**을 확장하려면 **npm run eject**을 하면 사용이 가능하다.
**redux** 부모 자식간 이벤트를 이동시킬 때 사용한다. 중앙에 데이터 저장소를 만들고 모든 컴포넌트는 중앙 저장소와 직접 연결되어 저장소의 데이터가 변경될 때 연결된 모든 데이터를 수정해준다.
**react server side rendering**
서버사이드에서 페이지를 완성하고 클라이언트 단으로 보낸다. 자바스크립트 특유의 로딩이 필요없는 애플리케이션 같은 특성을 유지할 수 있다. 검색 로봇 등에게 친화적인 기술이다.
**react native** 하나의 코드로 모든 플랫폼에서 동작하는 앱을 만들 수 있다.