생활코딩 : 리액트편을 보고 작성된 게시글 입니다.
React 만을 이용하여 READ/CREATE/UPDATE/DELETE 구현가능!
Read : 이전까지 계속 해오던 기능!
Create / Update / Delete
목표
1단계 버튼 컴포넌트 만들기
링크와 버튼으로 버튼 제작
App.js
render()함수 내 버튼을 만들기를 원하는 부분에 추가
<ul>
<li><a href="/create">create</a></li>
<li><a href="/update">update</a></li>
<li><input type="button" value="delete"></input></li>
</ul>
주의* : delete기능은 링크로 구현하지 않는 것이 좋음
컴포넌트로 만들어주기
컴포넌트 명 : <Control>
src/component/ 경로상에 Control.js파일을 만들어서 컴포넌트를 제작
Control.js
import React, { Component } from 'react';
class Control extends Component {
render(){
return(
<ul>
<li><a href="/create">create</a></li>
<li><a href="/update">update</a></li>
<li><input type="button" value="delete"></input></li>
</ul>
);
}
}
export default Control; //외부에서 사용을 허용
App.js에서 Control을 import하고, 컴포넌트로 대체해주기
import Control from './components/Control';
class App extends Component {
constructor(props){
super(props);
this.state ={
//생략
]
}
}
render(){
//생략
return (
<div className="App">
//생략 <Subject></Subject><TOC></TOC>
<Control></Control>
//생략 <Content></Content>
</div>
);
}
}
export default App;
2단계 버튼을 누르면 mode변경
onClick 과 onChangeMode(사용자 정의함수) 를 이용해서
하위 컴포넌트인 Control.js 에서 버튼이 눌릴때 상위 컴포넌트인 App.js의 State mode를 변경해줌
App.js
<Control onChangeMode={function(_mode){
this.setState({
mode : _mode
})
}.bind(this)}></Control>
onChangeMode 함수를 만들어 매개변수로 전달된 _mode변수로 mode를 변경해줌
Control.js
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; //외부에서 사용을 허용
3단계 mode에 맞추어 Content를 변경
mode 에 맞추어 CreateContent / ReadContent가 다르게 보여져야 하므로
기존의 Content 컴포넌트의 이름을 ReadContent로 변경하고 CreateContent를 새로 만들어줌
CreateContent.js
import React, { Component } from 'react';
class CreateContent extends Component{
render(){
return(
<article>
<h2>Create</h2>
</article>
);
}
}
export default CreateContent; //외부에서 사용을 허용
버튼을 누를때 변경되는 mode에 맞추어 create mode의 경우
Content가 표시되는 부분에 ReadContent대신 CreateContent 컴포넌트를 띄워주기
즉, Content부분은 이제 가변적으로 다른 컴포넌트가 띄워지므로 변수를 사용함 (_article 변수를 사용해서 상황에 따라 다른 컴포넌트를 담아줌)
if-else문을 이용해서 State.mode가 어떤 상태인지 확인하고, 그에 따라 <ReadContent> <CreateContent>를 적절하게 사용
App.js
import React, { Component } from 'react';
import './App.css';
import TOC from "./components/TOC";
import Subject from "./components/Subject"
import ReadContent from './components/ReadContent';
import Control from './components/Control';
import CreateContent from './components/CreateContent';
class App extends Component {
constructor(props){
super(props);
this.state ={
mode : 'read',
subject : {title:"WEB", sub:"world wide web!"},
selected_content_id : 2,
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">
//생략
{_article}
</div>
);
}
}
export default App;
welcome , read mode : Read Content
create mode : CreateContent
결과
Create를 누르면 아까 작성해둔 CreateContent부분 <h2>Create</h2>가 출력됨을 확인
4단계 CreateContent작성
입력을 받는 from작성
import React, { Component } from 'react';
class CreateContent extends Component{
render(){
return(
<article>
<h2>Create</h2>
<form action="/create_process" method="post">
<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; //외부에서 사용을 허용
다음과 같이 입력값을 넘겨줄 수 있는 form태그를 이용하여
title/ desc를 적는 텍스트공간과 submit버튼을 만들어준다. 그리고 post방식으로 넘겨줌
이때 , action을 통해 다른 page(create_process)로 넘기지 않고 바로 해당 페이지에서 해결해 주기 위해 onSubmit함수를 사용
onSubmit event
submit버튼을 포함하고 있는 from tag안에 onSubmit event를 설치하면, submit버튼을 눌렀을 때 해당 event가 실행됨
onSubmit event를 이용해 상위 컴포넌트인 App.js에 함수의 인자로 title과 desc값을 넘겨주는 방식으로 값을 전달
CreateContent.js
<form action="/create_process" method="post"
onSubmit={function(e){
e.preventDefault();
this.props.onSubmit(title변수,desc변수); //<-인자로 title과 desc값을 넘겨주기
}.bind(this)}>
App.js
else if(this.state.mode ==='create')
{
_article = <CreateContent onSubmit={function(_title,_desc){
//add content to this.state.contents
}.bind(this)}></CreateContent>
}
함수의 인자로 _title, _desc를 받아와 state내 content에 추가해주면됨
이제 실제로 CreateContent내에서 사용자 입력을 받은 title, desc를 어느 변수에 저장하는지 알아내면 완료
debugger(함수 내 디버그하고 싶은 곳에 작성 후)와 개발자도구 Source -> CreateContentjs->Scope내에서 찾아볼 수 있음
title에 적힌 값은 e.tartget.title.value
desc에 적힌 값은 e.target.desc.value
* e.target은 해당 함수를 사용한 태그 전체를 가리킴 : 여기서는 form태그
즉, CreateContent.js에서 임시의 변수로 두었던 곳을 변경 가능
CreateContent.js
<form action="/create_process" method="post"
onSubmit={function(e){
e.preventDefault();
this.props.onSubmit(e.target.title.value,e.target.desc.value); //<-인자로 title과 desc값을 넘겨주기
}.bind(this)}>
5단계 Add content to this.state.contents
App.js
else if(this.state.mode ==='create')
{
_article = <CreateContent onSubmit={function(_title,_desc){
//add content to this.state.contents
}.bind(this)}></CreateContent>
}
이제 _title, _desc로 넘겨받은 데이터를 기반으로
State부분에 데이터를 추가해봅시다.
constructor(props){
super(props);
this.max_content_id=3;
this.state ={
mode : 'read',
subject : {title:"WEB", sub:"world wide web!"},
selected_content_id : 2,
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'}
//여기에 데이터 추가
]
}
}
새로운 id값 부여하기
이미 있는 content의 max id값보다 1큰 수를 자동으로 지정해주어야함
max_id 값을 저장하는 변수 생성
constructor(props){
super(props);
this.max_content_id=3; //현재 최대 id값을 초기값으로 지정
this.state ={
//생략
}
}
state를 표현하는게 아니므로 state내부에 만들지 않는 것이 좋음
Contents는 배열 데이터 -> 배열에 데이터 추가
배열에 데이터를 추가하는 방법
1 . push
var arr = [1,2];
arr.push(3);
arr -> [1,2,3]
: 원본을 변경함
2. concate
var arr2 = [1,2];
var result = arr2.concat(3);
result -> [1,2,3]
arr2 -> [1,2]
: 원본을 변경하지 않고 새로운 배열을 return
즉, push가 아닌 concat방법을 쓰는 것이 적절함 나중에 개발 시 퍼포먼스를 높일때 후자가 훨씬 좋음!
엄밀히 말하면 push/concat자체의 차이보다는 특정 값을 수정할 때는 원본을 수정하는 것이 아니라 복제를 만들어 수정한후 원본에 대입시켜 주는 것이 좋음
concat방법 사용시
else if(this.state.mode ==='create')
{
_article = <CreateContent onSubmit={function(_title,_desc){
this.max_content_id = this.max_content_id+1; //1을 추가하여 id 부여
//concat방법을 이용하여 새로운 변수에 content를 저장
var _contents = this.state.contents.concat(
{id:this.max_content_id, title:_title, desc:_desc}
);
//새로 생성한 content를 state로 set
this.setState(
{
contents : _contents
}
);
}.bind(this)}></CreateContent>
+추가 ) Push방법을 사용시 복제를 이용하는법
Array.from(this.state.contents)를 이용하여 원본의 복제를 생성 후
복제에 값을 push해서 수정해준 후에 복제품을 원본에 대입하는 형식으로 변경
else if(this.state.mode ==='create')
{
_article = <CreateContent onSubmit={function(_title,_desc){
this.max_content_id = this.max_content_id+1; //1을 추가하여 id 부여
//_contents변수에 복제를 생성
var _contents = Array.from(this.state.contents);
//복제에 새로운 값을 push
_contents.push({id:this.max_content_id, title:_title, desc:_desc});
//복제를 원본에 덮어씌워 수정
this.setState(
{
contents : _contents
}
);
}.bind(this)}></CreateContent>
결과
새로 TOC가 추가된 모습
최종
App.js
import React, { Component } from 'react';
import './App.css';
import TOC from "./components/TOC";
import Subject from "./components/Subject"
import ReadContent from './components/ReadContent';
import Control from './components/Control';
import CreateContent from './components/CreateContent';
class App extends Component {
constructor(props){
super(props);
this.max_content_id=3; //현재 최대 id값을 초기값으로 지정
this.state ={
mode : 'read',
subject : {title:"WEB", sub:"world wide web!"},
selected_content_id : 2,
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){
//add content to this.state.contents
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)}></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;
CreateContent.js
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();
this.props.onSubmit(e.target.title.value, e.target.desc.value);
}.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; //외부에서 사용을 허용
ReadContent.js
import React, { Component } from 'react';
class ReadContent extends Component{
render(){
return(
<article>
<h2>{this.props.title}</h2>
{this.props.desc}
</article>
);
}
}
export default ReadContent; //외부에서 사용을 허용
Control.js
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; //외부에서 사용을 허용
참고영상
생활코딩
- React - 19.1. create 구현 : 소개
- React - 19.2. create 구현 : mode 변경 기능
- React - 19.3. create 구현 : mode 전환 기능
- React - 19.4. create 구현 : form
- React - 19.5. create 구현 : onSubmit 이벤트
- React - 19.6. create 구현 : contents 변경
- React - 19.7. create 구현 : shouldComponentUpdate
- React - 19.8. create 구현 : immutable
'Frontend > React' 카테고리의 다른 글
[React] 마무리 정리 + 코드모음 (0) | 2020.11.18 |
---|---|
[React] Update, Delete기능 구현 (0) | 2020.11.18 |
[React] Event (0) | 2020.11.18 |
[React] State (0) | 2020.11.17 |
[React] Component (0) | 2020.11.17 |