props vs state
| Props | State |
| read-only | asynchronous |
| can not be modified | can be modified using this.setState |
- 상위 component가 하위 component에 명령할 때 Props를 사용하고, 하위 component가 상위 component에
명령할 때는 이벤트를 사용한다.
- Props는 read-only. 자신의 하위에 포함된 것들은 변경할 수 있지만, 자신의 상위에 포함된 것들은 변경할 수 없다.
이 경우 이벤트를 사용해 간접적으로 명령해 바꾼다.
- props는 상위 component가 하위 component에게 데이터 값만 전달하는 반면, state는 component 내부에서
선언해 이벤트를 생성, 변경한다.
<Create>
1) mode 변경
import React, { Component } from 'react';
import TOC from "./components/TOC";
import Content from "./components/Content";
import Subject from "./components/Subject";
import Control from "./components/Control";
class App extends Component {
constructor(props) {
super(props);
this.state = {
mode:'read',
selected_content_id:2,
subject:{title:'WEB', sub:"World Wide Web!"},
welcome:{title:'Welcome',desc:'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() {
var _title, _desc = null;
if(this.state.mode === 'welcome') {
_title = this.state.welcome.title;
_desc = this.state.welcome.desc;
}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;
}
}
return (
<div className="App">
<Subject
title={this.state.subject.title}
sub={this.state.subject.sub}
onClickPage={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>
);
}
}
export default App;
import React, { Component } from 'react';
class Control extends Component {
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;
- Control이라는 component를 만들어 create, update, delete를 누를 수 있게 만든다.(delete는 button)
- onChangeMode 이벤트를 활용해 create, update, delete를 누를 때마다 해당 mode로 state값이 바뀌게 한다.
2) mode 전환
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";
class App extends Component {
constructor(props) {
super(props);
this.state = {
mode:'read',
selected_content_id:2,
subject:{title:'WEB', sub:"World Wide Web!"},
welcome:{title:'Welcome',desc:'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() {
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></CreateContent>
}
return (
<div className="App">
<Subject
title={this.state.subject.title}
sub={this.state.subject.sub}
onClickPage={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;
import React, { Component } from 'react';
class ReadContent extends Component {
render() {
return (
<article>
<h2>{this.props.title}</h2>
{this.props.desc}
</article>
);
}
}
export default ReadContent;
import React, { Component } from 'react';
class CreateContent extends Component {
render() {
return (
<article>
<h2>Create</h2>
<form>
</form>
</article>
);
}
}
export default CreateContent;
- mode가 전환되면 content값이 해당 mode의 content로 바뀌게 한다.(mode가 read면 read content로,
mode가 create면 create content로 바뀌게)
- _article이라는 변수 만들어 mode마다 바뀌는 content 핸들링할 수 있게 한다.
3) form
CreateContent component의 비워뒀던 form을 채우자
import React, { Component } from 'react';
class CreateContent extends Component {
render() {
return (
<article>
<h2>Create</h2>
<form action="/create_process" method="post"
onSubmit={function(e){
e.preventDefault();
alert('Submit!!!');
}.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;
- textarea: 입력할 text가 여러 줄일 때 사용
4) onSubmit 이벤트
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";
class App extends Component {
constructor(props) {
super(props);
this.state = {
mode:'create',
selected_content_id:2,
subject:{title:'WEB', sub:"World Wide Web!"},
welcome:{title:'Welcome',desc:'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() {
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) {
console.log(_title, _desc);
}.bind(this)}></CreateContent>
}
return (
<div className="App">
<Subject
title={this.state.subject.title}
sub={this.state.subject.sub}
onClickPage={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;
import React, { Component } from 'react';
class CreateContent extends Component {
render() {
return (
<article>
<h2>Create</h2>
<form action="/create_process" method="post"
onSubmit={function(e){
e.preventDefault();
debugger;
this.props.onSubmit(
e.target.title.value,
e.target.desc.value
);
alert('Submit!!!');
}.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;
- CreateContent component에서 e.target 활용- App.js CreateContent에 onClick 이벤트 추가
5) Contents 변경
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";
class App extends Component {
constructor(props) {
super(props);
this.max_content_id = 3;
this.state = {
mode:'create',
selected_content_id:2,
subject:{title:'WEB', sub:"World Wide Web!"},
welcome:{title:'Welcome',desc:'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() {
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) {
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}
)
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}
onClickPage={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;
- state에 값을 추가할 때는 push와 같이 original data를 변경하는 것을 쓰지 말고, concat처럼 original data를 변경하지 않고 새로운 data를 추가하는 것을 쓴다.