1. 누르면 mode가 변하는 작업
미리 진행되어 있는 작업에 components를 추가하려고 한다. components 파일의 하부에 Control.js를 만들어서 아래 코드를 삽입한다.
import React, { Component } from 'react';
class Control extends Component {
render() {
console.log('Control render')
return (
<ul>
<li><a href="/create" onClick={function(e) {
e.preventDefault();
this.props.onChangeMode('create');
}.bind(this)}>create</a></li>
<li><a href="/update" onClick={function(e) {
e.preventDefault();
this.props.onChangeMode('update');
}.bind(this)}>update</a></li>
<li><input onClick={function(e) {
e.preventDefault();
this.props.onChangeMode('delete');
}.bind(this)} type="button" value="delete"></input></li>
</ul>
);
}
}
export default Control;
그리고 App.js에 아래처럼 Control을 넣어준다.
<div className="App">
<Subject
title = {this.state.subject.title}
sub = {this.state.subject.sub}
onChangePage = {function() {
this.setState({ mode: "welcome"});
}.bind(this)}
>
</Subject>
{/* 자식 */}
<TOC
onChangePage={function(id) {
this.setState({
mode: "read",
selected_content_id: Number(id)
});
}.bind(this)}
data = {this.state.contents}
></TOC>
<Control onChangeMode={function(_mode) {
this.setState({
mode: _mode
})
}.bind(this)}></Control>
<Content title={_title} desc={_desc}></Content>
</div>
코드를 적용하게 되면 아래 그림처럼 create와 update 그리고 delete가 생길 것이다. 각 버튼을 클릭하게 되면 mode가 바뀌는 것을 알 수 있다.
2. mode가 변하고 해당하는 state 가져오기
우선 CreateContent.js라는 파일을 components에 만든다. 그리고 원래 Contents.js는 ReadContent.js로 변경한다.
import React, { Component } from 'react';
class CreateContent extends Component {
render() {
console.log('contents render')
return (
<article>
<h2>Create</h2>
<form action="/create_process" method="post"
onSubmit={function(e) {
e.preventDefault();
alert("create!!")
}.bind(this)}
>
<p><input type="text" name="title" placeholder="title"></input></p>
<p><textarea name="desc" placeholder="description"></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default CreateContent;
App.js에서 아래 코드처럼 if문에 따라 mode에 따라서 변하는 값을 수정해 주면된다.
render() {
console.log('App render')
let _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") {
for(let i = 0; this.state.contents.length; i++) {
let data = this.state.contents[i];
if(data.id === this.state.selected_content_id) {
_title = data.title;
_desc = data.desc;
break;
}
}
_article = <ReadContent title={_title} desc={_desc}></ReadContent>
} else if(this.state.mode === "create") {
_article = <CreateContent></CreateContent>
}
이렇게 되면 create를 누르고 submit 버튼을 눌렀을 때 alert 창이 뜰 것이다.
3. form태그에 입력한 값들을 state.contents 배열에 추가하기
state.contents에 배열을 추가하려면 App.js에서 onSubmit이라는 함수를 만들어줘서 사용하면 된다.
App.js
import React, { Component } from 'react';
import TOC from "./components/TOC"
import ReadContent from "./components/ReadContent"
import CreateContent from './components/CreateContent';
import Subject from "./components/Subject"
import Control from "./components/Control"
import './App.css';
class App extends Component {
// render 함수보다 먼저 실행이되면서 초기화시켜주고 싶은 것은 constructor안에다가 짜라
// 부모
constructor(props) {
super(props);
this.max_content_id = 3;
// 초기화 끝내고 state를 초기화 시킨다.
this.state = {
mode: "create",
selected_content_id: 1,
subject: {title: "Web", sub: "World Wide Web!"},
welcome: {title:"Welcome", decs:"Hello, React!!!"},
contents:[
{id: 1, title: 'HTML', desc: 'HTML is for information'},
{id: 2, title: 'CSS', desc: 'CSS is for design'},
{id: 3, title: 'JAVASCRIPT', desc: 'JAVASCRIPT is for interactive'}
]
}
}
render() {
console.log('App render')
let _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") {
for(let i = 0; this.state.contents.length; i++) {
let data = this.state.contents[i];
if(data.id === this.state.selected_content_id) {
_title = data.title;
_desc = data.desc;
break;
}
}
_article = <ReadContent title={_title} desc={_desc}></ReadContent>
} else if(this.state.mode === "create") {
_article = <CreateContent onSubmit={function(_title, _desc) {
this.max_content_id = this.max_content_id + 1;
// push는 원본데이터를 변경함.
// this.state.contents.push({ id: this.max_content_id, title: _title ,desc: _desc});
// this.setState({
// contents: this.state.contents
// })
// concat으로 해야 원본 데이터를 바꾸지 않는다.
let _contents = this.state.contents.concat(
{ id: this.max_content_id, title: _title ,desc: _desc}
)
this.setState({
contents: _contents
})
console.log(_title, _desc)
}.bind(this)}></CreateContent>
}
return (
<div className="App">
<Subject
title = {this.state.subject.title}
sub = {this.state.subject.sub}
onChangePage = {function() {
this.setState({ mode: "welcome"});
}.bind(this)}
>
</Subject>
{/* 자식 */}
<TOC
onChangePage={function(id) {
this.setState({
mode: "read",
selected_content_id: Number(id)
});
}.bind(this)}
data = {this.state.contents}
></TOC>
<Control onChangeMode={function(_mode) {
this.setState({
mode: _mode
})
}.bind(this)}></Control>
{_article}
</div>
);
}
}
export default App;
위 코드에서 else if(this.state.mode === "create") 이 부분에 <CreateContent onSubmit ~~ 이 부분에 정의되어있다. 그리고 this.max_content_id는 사용자에게 보여주는 것이 아니라 내부에서만 참조하기 위해 만든 변수라서 state밖에 선언해주어도 된다.
이제 만들어진 onSubmit함수를 CreateContent.js에서 this.props.onSubmit을 활용하여 가져오면 된다. 아래에 설명은 주석을 달아두었다.
import React, { Component } from 'react';
class CreateContent extends Component {
render() {
console.log('contents render')
return (
<article>
<h2>Create</h2>
<form action="/create_process" method="post"
onSubmit={function(e) {
e.preventDefault();
// e의 속성 값을 이용해서 e.target.id값 으로 받아온다.
this.props.onSubmit(e.target.title.value, e.target.desc.value);
alert("create!!")
}.bind(this)}
>
<p><input type="text" name="title" placeholder="title"></input></p>
<p><textarea name="desc" placeholder="description"></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default CreateContent;
shouldComponentUpdate()란? (push(원본 변형) concat(원본 유지)의 차이 설명)
간단한 예를 들기 위해 TOC.js에서 render() 함수 위에 shouldComponenteUpate(newProps, newState)를 사용하였다.
import React, { Component } from 'react';
class TOC extends Component {
shouldComponentUpdate(newProps, newState) {
console.log(newProps.data, this.props.data)
return true;
}
render() {
let lists = [];
let data = this.props.data;
for(let i = 0; i < data.length; i++) {
lists.push(
<li key = {data[i].id}>
<a
href={"/content/" + data[i].id}
//data-id = {data[i].id}
onClick = {function(id, e) {
e.preventDefault();
this.props.onChangePage(id);
}.bind(this, data[i].id)}
>{data[i].title}</a>
</li>);
}
console.log('TOC render')
return (
<nav>
<ul>
{lists}
</ul>
</nav>
);
}
}
export default TOC;
위에서 보이는 것 같이 console.log()를 찍었을 때 shouldComponentUpdate는 newProps와 newState를 받아 올 수 있다. 그리고 또 하나의 특징은 return 값이 기본적으로 true이고 true이면 render 함수가 호출되고 false를 사용하면 아래 render 함수는 나타나지 않는다. 즉, render 함수보다 shouldComponentUpdate 함수가 먼저 실행된다는 것을 알 수 있다.
여기서! 위에 내용을 넣을 때 push를 하는 것과 concat을 쓰는 차이가 나온다. 만약 push를 쓰면 this.props.data와 newProps.data가 같아서 shouldComponentUpdate를 이용하지 못할 수도 있다. 아래 코드처럼 쓰고 싶을 때 못할 수도 있다는 말이다.
class TOC extends Component {
shouldComponentUpdate(newProps, newState) {
console.log(newProps.data, this.props.data)
if(this.props.data === newProps.data) {
return false;
} else {
return true;
}
}
이후 진행하는 프로젝트에서 저는 concat을 활용할 것입니다.
* shouldComponentUpdate를 쓰는 사람을 위한 code tips
배열에서 복사 array.from(배열)
let a = [2, 3];
let b = Array.from(a);
console.log(b); // [2, 3]
console.log(a === b); // false
오브젝트에서 복사 Object.assign(오브젝트)
let a = {2, 3};
let b = Object.assign(a);
console.log(b); // {2, 3}
console.log(a === b); // false
두 개 모두 완벽히 같은 것은 아니고 내용만 같은 것이라서 등호를 해보면 false가 나온다.
'학습노트 > React' 카테고리의 다른 글
[React] (12) Delete 기능 구현 (CRUD) - CRUD 구현된 github (0) | 2020.10.22 |
---|---|
[React] (11) Update 기능 구현 (CRUD) (0) | 2020.10.22 |
[React] (9) 이벤트 state, props, event 만들기, render function, bind 함수, setState (0) | 2020.10.21 |
[React] (8) Key - Warning: Each child in a list should have a unique "key" prop. (0) | 2020.10.20 |
[React] (7) State 사용 (0) | 2020.10.19 |