생활코딩 : 리액트 편을 보고 작성된 게시글 입니다.
Update , Delete기능 구현
코드 정리
먼저 시작전에 render내의 if-else문으로 content의 내용을 결정짓는 내용이 너무 길기 때문에
다른 함수로 분리한 후 -> getContent(){}
_article변수를 return값으로 뱉게하고, _article이 쓰이던 content부분에
getContent() 를 호출하도록 변경
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'}
]
}
}
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){
//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 _article;
}
render(){
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>
{this.getContent()}
</div>
);
}
}
export default App;
Update기능
Update : read + create 기능
원래 쓰여있던 내용을 불러옴 (read)
내용 수정 후 작성(변경 내용 반영) (create)
1단계 기본적인 틀 만들기
Update는 Create와 유사하므로
CreateContent.js를 복사하여 이름만 UpdateContent.js로 변경한 파일을 생성해 주고
UpdateContent.js
import React, { Component } from 'react';
class UpdateContent extends Component{
render(){
return(
<article>
<h2>Update</h2>
<form action="/update_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 UpdateContent; //외부에서 사용을 허용
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';
import UpdateContent from './components/UpdateContent';
class App extends Component {
constructor(props){
//생략
}
getContent(){
var _title, _desc, _article = null;
if(this.state.mode === 'welcome')
{
//생략
}
else if(this.state.mode === 'read')
{
//생략
}
else if(this.state.mode ==='create')
{
//생략
}
else if(this.state.mode ==='update')
{
_article = <UpdateContent 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)}></UpdateContent>
}
return _article;
}
render(){
return (
<div className="App">
//생략
</div>
);
}
}
export default App;
App.js에서도 mode가 update인지 확인하는 부분을 create 부분과 동일하게 일단 추가해 준 후
Component이름만 UpdateContent로 변경해줌
2단계 본문 내용 불러오기
Update 버튼을 눌렀을 때 form 태그에 update를 하려는 항목의 내용이 그대로 불려와야함
현재 선택한 항목의 내용을 불러오는 것은 이미 read를 구현할 때 했던 것 ( 목차를 클릭하면 해당하는 id의 번호 출력)
App.js의 getContent()내의 내용
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>
}
이 부분을 여러번 사용할 것이니 함수로 따로 제작
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){
return data;
}
i=i+1;
}
}
data를 title, desc로 나누지 않고 그대로 data를 return
App.js의 getContent()내의 내용도 getReadContent함수를 사용하는 것으로 변경
else if(this.state.mode === 'read')
{
var _content = this.getReadContent();
_article = <ReadContent title={_content.title} desc={_content.desc}></ReadContent>
}
함수를 사용해서 return 된 data를 _content변수에 저장한 후 ._title , ._desc에 접근해서 사용
이제 제작된 getReadContent함수를 Update에 사용해서 불러온 후 <UpdateContent>의 속성(props)로 전달
App.js getContent()함수 내부
else if(this.state.mode ==='update')
{
_content = this.getReadContent();
_article = <UpdateContent data={_content} 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)}></UpdateContent>
}
본문내용을 보여주는 것은
props로 전달된 data.title을 value에 넣어주면 update를 눌렀을때 알맞는 content의 제목이 date로 잘 전달된 것을 확인 가능
UpdateContent.js
<p><input
type="text"
name="title"
placeholder="title"
value={this.props.data.title}
></input></p>
<p><textarea name="desc" placeholder="description"></textarea></p>
<p><input type="submit"></input></p>
3단계 본문 내용 수정
하지만 이 방법을 사용하면 수정이 불가능함
props의 데이터는 read only만 가능 -> 가변적인 데이터인 State로 prop를 바꾸어주어야함
UpdateContent.js
constructor(props){
super(props);
this.state={
title:this.props.data.title
}
}
Constructor를 추가하고 state에 title : this.props.data.title을 추가하여, data로 받아온 정보를 state화 시킴
onChange함수를 이용해서 State를 조작할 수 있음
UpdateContent.js
<p><input
type="text"
name="title"
placeholder="title"
value={this.state.title}
onChange={function(e){
this.setState(
{title:e.target.value}
);
}.bind(this)}
></input></p>
e.target.value : e.target은 input tag 전체를 의미하고 value는 그에 적히는 값이므로
title을 input tag에 적히는 값으로 변경해주는 onChange함수 + setState를 사용
+또한, value = {this.state.title}로 변경해주어야함 (prop는 변경이 불가능한 값이므로 state로 바꾸어줌)
desc인 textarea에도 그대로 적용해주면 됨
UpdateContent.js
import React, { Component } from 'react';
class UpdateContent extends Component{
constructor(props){
super(props);
this.state={
title:this.props.data.title,
desc : this.props.data.desc
}
}
render(){
return(
<article>
<h2>Update</h2>
<form action="/update_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"
value={this.state.title}
onChange={function(e){
this.setState(
{title:e.target.value}
);
}.bind(this)}
></input></p>
<p><textarea
name="desc"
placeholder="description"
value={this.state.desc}
onChange={function(e){
this.setState(
{desc:e.target.value}
)
}}
></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default UpdateContent; //외부에서 사용을 허용
약간의 refactoring
onChange 함수를 밖으로 빼내서 중복을 제거
inputFormHandler(e){
this.setState(
{[e.target.name]:e.target.value}
);
}
다음과 같이 만들어서 onChange함수 내에 대입
<p><input
type="text"
name="title"
placeholder="title"
value={this.state.title}
onChange={this.inputFormHandler.bind(this)}
></input>
</p>
<p><textarea
name="desc"
placeholder="description"
value={this.state.desc}
onChange={this.inputFormHandler.bind(this)}
></textarea>
</p>
.bind(this)를 항상 쓰지 않는 법
constructor(props){
super(props);
this.state={
title:this.props.data.title,
desc : this.props.data.desc
}
this.inputFormHandler = this.inputFormHandler.bind(this);
}
이렇게 추가해주면 inputFromHandler를 사용할때 뒤에 bind(this)를 항상 붙이지 않아도 됨
4단계 State 변경 ( 수정 내용 State에 반영)
UpdateContent.js
change가 발생하는 content의 id값을 알기위해 state추가
constructor(props){
super(props);
this.state={
id : this.props.data.id,
title:this.props.data.title,
desc : this.props.data.desc
}
this.inputFormHandler = this.inputFormHandler.bind(this);
}
form태그에 id부여
<input type="hidden" name="id" value={this.state.id}></input>
id값은 수정하는 대상이 아니므로 hidden으로 설정해 보이지 않게함
form을 submit시 id값을 함께 App 컴포넌트로 넘겨줌
<form action="/update_process"
method="post"
onSubmit={function(e){
e.preventDefault();
this.props.onSubmit(this.state.id,this.state.title, this.state.desc);
}.bind(this)}>
App컴포넌트는 id값,title값,desc값을 넘겨받아서
id값으로 수정을 원하는 content인지 확인한 후 맞으면 State의 content를 수정 (setState)
App.js
else if(this.state.mode ==='update')
{
_content = this.getReadContent();
_article = <UpdateContent data={_content} onSubmit={function(_id,_title,_desc){
//update content to this.state.contents
var _contents = Array.from(this.state.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;
}
this.setState(
{
contents : _contents
}
);
}.bind(this)}></UpdateContent>
}
Array.from(this.state.contents) : array를 복제해서 생성
//수정할때는 원본이 아닌 복제품을 수정해서 완료후 원본에 대입하는 방식을 써야함
while문을 이용해 content를 증가시키면서 수정을 원하는 id와 일치하면 복제한 contents를 수정
그 후 set State를 이용하여 state를 바꿔줌
5단계 제출버튼 누른 후 read로 mode변경
제출 버튼을 누르면 update모드( form창이 보임) 가 아닌 read mode(본문 내용창)이 보이도록 설정
App.js
else if(this.state.mode ==='update')
{
_content = this.getReadContent();
_article = <UpdateContent data={_content} onSubmit={function(_id,_title,_desc){
//update content to this.state.contents
var _contents = Array.from(this.state.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;
}
this.setState(
{
contents : _contents,
mode : 'read'
}
);
}.bind(this)}></UpdateContent>
}
setState부분에 mode : 'read'추가
create시에도 똑같이 변경해줌
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,
mode : 'read',
selected_content_id :this.max_content_id
}
);
}.bind(this)}></CreateContent>
}
setState에
mode : 'read'를 추가하고 read시 현재 추가한 글을 보여지게 하도록 방금 추가한 id를 selected_content_id로 변경해줌
전체코드
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';
import UpdateContent from './components/UpdateContent';
class App extends Component {
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'}
]
}
}
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;
}
i=i+1;
}
}
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 _content = this.getReadContent();
_article = <ReadContent title={_content.title} desc={_content.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,
mode : 'read',
selected_content_id :this.max_content_id
}
);
}.bind(this)}></CreateContent>
}
else if(this.state.mode ==='update')
{
_content = this.getReadContent();
_article = <UpdateContent data={_content} onSubmit={function(_id,_title,_desc){
//update content to this.state.contents
var _contents = Array.from(this.state.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;
}
this.setState(
{
contents : _contents,
mode : 'read'
}
);
}.bind(this)}></UpdateContent>
}
return _article;
}
render(){
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>
{this.getContent()}
</div>
);
}
}
export default App;
UpdateContent.js
import React, { Component } from 'react';
class UpdateContent extends Component{
constructor(props){
super(props);
this.state={
id : this.props.data.id,
title:this.props.data.title,
desc : this.props.data.desc
}
this.inputFormHandler = this.inputFormHandler.bind(this);
}
inputFormHandler(e){
this.setState(
{[e.target.name]:e.target.value}
);
}
render(){
return(
<article>
<h2>Update</h2>
<form action="/update_process"
method="post"
onSubmit={function(e){
e.preventDefault();
this.props.onSubmit(this.state.id,this.state.title, this.state.desc);
}.bind(this)}>
<input type="hidden" name="id" value={this.state.id}></input>
<p><input
type="text"
name="title"
placeholder="title"
value={this.state.title}
onChange={this.inputFormHandler}
></input></p>
<p><textarea
name="desc"
placeholder="description"
value={this.state.desc}
onChange={this.inputFormHandler}
></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default UpdateContent; //외부에서 사용을 허용
Delete기능
Delete시에는 화면이 전환되지 않음
따라서 if / else문을 이용해서 delete가 아닐 때만 setState mode : _mode로 변경시켜 Create/UpdateContent.js가 실행되도록 함
<Control onChangeMode={function(_mode){
if(_mode === 'delete'){
}else{
this.setState({
mode : _mode
})
}
}.bind(this)}></Control>
정말 삭제할건지 물어보기
window.confirm
확인을 누르면 true, cancle을 누르면 false를 반환함
즉, true일 때, while문을 selcted_content_id와 같은 id를 보유한 contents를 검사해 삭제함
<Control onChangeMode={function(_mode){
if(_mode === 'delete'){
if(window.confirm("정말 삭제하실 건가요?")){
//삭제
}
}else{
this.setState({
mode : _mode
})
}
}.bind(this)}></Control>
splice(a,b)
a 번호부터 b개를 삭제해주는 함수
<Control onChangeMode={function(_mode){
if(_mode === 'delete'){
if(window.confirm("정말 삭제하실 건가요?")){
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
})
}else{
this.setState({
mode : _mode
})
}
}.bind(this)}></Control>
물론 여기서도 복제한 Array를 이용해 삭제 한 후 setState를 이용하여 반영해줌
그 후 해당 게시글이 삭제되었으니 welcome모드로 변경해줌
alert로 삭제가 완료되었다는 문구출력
<Control onChangeMode={function(_mode){
if(_mode === 'delete'){
if(window.confirm("정말 삭제하실 건가요?")){
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("삭제가 완료되었습니다.");
}
}else{
this.setState({
mode : _mode
})
}
}.bind(this)}></Control>
정상적으로 삭제된 모습
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';
import UpdateContent from './components/UpdateContent';
class App extends Component {
constructor(props){
super(props);
this.max_content_id=3;
this.state ={
mode : 'welcome',
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'}
]
}
}
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;
}
i=i+1;
}
}
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 _content = this.getReadContent();
_article = <ReadContent title={_content.title} desc={_content.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,
mode : 'read',
selected_content_id :this.max_content_id
}
);
}.bind(this)}></CreateContent>
}
else if(this.state.mode ==='update')
{
_content = this.getReadContent();
_article = <UpdateContent data={_content} onSubmit={function(_id,_title,_desc){
//update content to this.state.contents
var _contents = Array.from(this.state.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;
}
this.setState(
{
contents : _contents,
mode : 'read'
}
);
}.bind(this)}></UpdateContent>
}
return _article;
}
render(){
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){
if(_mode === 'delete'){
if(window.confirm("정말 삭제하실 건가요?")){
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
})
}
}else{
this.setState({
mode : _mode
})
}
}.bind(this)}></Control>
{this.getContent()}
</div>
);
}
}
export default App;
참고영상
생활코딩
- React - 20.1 update 구현
- React - 20.2 update 구현 : form
- React - 20.3 update 구현 : state변경
- React - 21 delete 구현
'Frontend > React' 카테고리의 다른 글
[React] 유형테스트 만들기(1) - 기획 (0) | 2021.03.02 |
---|---|
[React] 마무리 정리 + 코드모음 (0) | 2020.11.18 |
[React] Create기능 구현 (0) | 2020.11.18 |
[React] Event (0) | 2020.11.18 |
[React] State (0) | 2020.11.17 |