(그전에 수정할 내용)
이고잉은 오류가 안뜨는데 나만 떠는 오류.. JSX문법에 안맞는다는 말인데.. 그래서 뭐가 틀렸단말인가?! 봤더니..
<사용자정의_태그>를 작성하는 경우 ' _ '를 사용하면 안된다는 오류였다. <Update_Contents>컴포넌트를 생성하면서 한줄이 추가되는걸 보고 알았다. 그동안은 따라가느라 정신이 없었나보다. 암튼 그동안 작성했던 컴포넌트의 ' _ '를 모두 삭제하니까 말끔해졌다.~
(이제 고고~ 얼마 안남았다~)
https://opentutorials.org/module/4058/24861
1. Update
1) 컴포넌트를 추가하고 연동하기
UpdateContents.js
UpdateContents.js에 <UpdateContents>컴포넌트를 만들고 app.js에 연동한다.
import React, {Component} from 'react';
class UpdateContent extends Component{
render(){
console.log('Update render');
return(
<article>
<h2>Update</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 UpdateContent;
app.js
mode가 update일 경우, <UpdateContents>컴포넌트가 출력되도록 render(){}안에 else if{ ... }를 작성한다.
(초략)
import Control from "./component/Control"
(중략)
}else if(this.state.mode === 'update'){
_content = <UpdateContent onSubmit={function(_title, _desc){
var _new_content = this.state.content.concat({
id:this.count_contents_id, title:_title, desc:_desc
});
this.setState({
content: _new_content
});
}.bind(this)}></UpdateContent>
}
(대략 ㅋ)
2) 리팩토링
(1) render(){ ..안에서 복잡해진 코드를.. }, 별도의 함수로 빼내기
코드의 용도는 조건문을 통해 mode의 값을 확인하고, 콘텐츠 영역에 각 mode에 맞는 콘텐츠를 출력하는 역할을 한다.
app.js - 콘텐츠 출력 조건문을 getContent(){} 함수로 render()앞으로 빼낸다
import React, {Component} from 'react';
import './App.css';
import TOC from "./component/TOC"
...
class App extends Component{
...
}
getContents(){
console.log('- getContents()');
var _title, _desc,_content = null;
if(this.state.mode === 'welcome'){
_title=this.state.welcome.title;
_desc=this.state.welcome.desc;
_content = <ReadContent title={_title} desc={_desc}></ReadContent>
}else if(this.state.mode === 'read'){
var i =0;
while(i<this.state.content.length){
var data = this.state.content[i];
if(data.id === this.state.selected_content_id){
_title=data.title;
_desc=data.desc;
break;
}
i++;
}
_content = <ReadContent title={_title} desc={_desc}></ReadContent>
}else if(this.state.mode === 'create'){
_content = <CreateContent onSubmit={function(_title, _desc){
var _new_content = this.state.content.concat({
id:this.count_contents_id, title:_title, desc:_desc
});
this.setState({
content: _new_content
});
}.bind(this)}></CreateContent>
}else if(this.state.mode === 'update'){
_content = <UpdateContent onSubmit={function(_title, _desc){
var _new_content = this.state.content.concat({
id:this.count_contents_id, title:_title, desc:_desc
});
this.setState({
content: _new_content
});
}.bind(this)}></UpdateContent>
}
return _content; // +'return' <- 새로 생성한 getContents의 역할
}
render(){
console.log('APP render');
return (
<div className="App">
<Subject ... ></Subject>
<TOC ... ></TOC>
<Control ... </Control>
{/* {_content} */} // <- 안에 있을때는 바로 썼지만 밖으로 뺏으니까, 다시 받는다.
{this.getContents()} // this. 으로 다시 받는다.
</div>
);
}
}
export default App;
(2) 콘텐츠 배열 중 클릭한 항목의 제목과 내용 출력하기
app.js
render(){ ...안에 있던 클릭한 항목의 제목과 내용 출력... }코드를 밖으로 빼서 getReadContent(){ ...여기에... }를 만들어, 이 함수를 읽기와 업데이트에 같이 사용할 수 있도록 한다.
#getCotent 위에
getReadContent(){
console.log('- getReadContent(선택한 콘텐츠 출력)');
var i =0;
while(i<this.state.content.length){
var data = this.state.content[i];
if(data.id === this.state.selected_content_id){
return data;
// _title=data.title; // <- _title는 이제 사용하지 않는다.
// _desc=data.desc;
//break;
}
i++;
}
//return data;
}
#조건문 안에 내용 수정
#결과적으로?, title={_cdata.title}를 하기위한 코드
else if(this.state.mode === 'read'){
var _cdata = this.getReadContent();
//*** 함수로 바뀐 부분!의 리턴(data)값을 담기위해 변수를 선언한다.
//debugger;
_content = <ReadContent title={_cdata.title} desc={_cdata.desc}></ReadContent>
//_cdata._title가 아니다! ㅋ
}
(중략)
#결과적으로?, data={_cdata}를 하기위한 코드
else if(this.state.mode === 'update'){
_cdata = this.getReadContent();
//*** 함수 재활용, 여기서 변수(_data)는 다시 선언할 필요가 없네?! 가 아니라 var만 뺐네..!
_content = <UpdateContent
data={_cdata}
onSubmit={function(_title, _desc){
var _new_content = this.state.content.concat({
id:this.count_contents_id, title:_title, desc:_desc
});
this.setState({
content: _new_content
});
}.bind(this)}></UpdateContent>
}
UpdateContents.js에 console.log(this.props.data);를 추가해서 값이 잘 넘어 오는지 확인한다.
3) UpdateContents 컴포넌트 form 작업
https://reactjs.org/docs/forms.html
(1) 폼안에 데이터 넣기 1차
1) value={this.props.title}이 아니라 value={this.props.data.title}
2) form안에 있는 this.props.data는 리액트가 값이 변경되지 않도록 개입한다.
3) UpdateContent 안에 생성자(props){state를 재설정하는}를 만들어서 form안에서 value={this.state.title} 로 받는다
UpdateContents.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(){
console.log(this.props.data);
console.log('Update render');
return(
<article>
<h2>Update</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" value={this.state.title}></input></p>
<p><textarea name="desc" placeholder="description" value={this.state.desc}></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default UpdateContent;
(2) 폼안에 데이터 넣기 2차
onChange={ function(e){...}.bind(this) }를 걸어준다! <- state와 <input>을 서로 연결하여 readOnly 상태를 벗어나기
...에 콘솔로그를 통해 찍히는 값을 확인해보면
console.log(e.target.value)
연결은 되었지만 콘솔에는 키보드를 누르는 순간 한글자씩만 출력되고 있는 것을 확인할 수 있다. 물론 화면에는 안찍히는 이유는 state에 반영이 안되었기 때문이겠다.
이거 예전, java배울때, 최종프로젝트에서 외부 editor.js 라이브러리 연동할때 만났던 이슈와 비슷한거 같다. 그때는 웹소캣을 통해 여러명이 실시간으로 문서를 편집할 수 있는거를 만들때 였는데.. 그때도 한자 한자 찍을때마다 넘겨서 화면에 반영하도록 만들었던 거 같다. 생코 예제도 예전 방법과 유사하게~ 한자 한자 찍을때마다 state에 반영시키고 있다.
그리고, 잊지말아야할 것! state 동기화는 항상 setState({a:'b'})
<input
type="text"
name="title"
placeholder="title"
value={this.state.title} //*연결은 되었지만 갱신은 안된다.
onChange={function(e){ //*갱신하기 위한 함수안에서
//** 변경되는 value를 반영하는 함수안에서 로그를 찍어보자.
// console.log(e.target.value)
//** 키보드를 누를때마다 state에 반영하여 화면에 출력되도록 한다.
this.setState({
title:e.target.value
});
}.bind(this)}
></input>
참고로 빨산 워닝~은.. 내가 생성자 안에 state에 두 개의 값(title과 desc)을 설정하고 onChange()는 title에만 적용해서이다. 아래 teatarea에도 onChange()를 추가하면 없어진다.
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(){
console.log(this.props.data);
console.log('Update render');
return(
<article>
<h2>Update</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"
value={this.state.title} //연결은 되었지만 갱신은 안된다.
onChange={function(e){ //갱신하기 위한 함수안에서
// console.log(e.target.value) // 변경되는 value를 반영하는 함수안에서 로그를 찍어보자.
// 키보드를 누를때마다 state에 반영하여 화면에 출력되도록 한다.
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
});
}.bind(this)}
></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default UpdateContent;
(3) javascript 최신문법 []를 사용하여 리펙토링
- onChange()={ ...이 코드!... }
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
}
}
inputFormHandler(e){
this.setState({
//title:e.target.value
[e.target.name]:e.target.value
});
}
render(){
console.log(this.props.data);
console.log('Update render');
return(
<article>
<h2>Update</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"
value={this.state.title}
//onChange={function(e){this.inputFormHandler}.bind(this)} //no b!
onChange={this.inputFormHandler.bind(this)}
></input></p>
<p><textarea
name="desc"
placeholder="description"
value={this.state.desc}
onChange={this.inputFormHandler.bind(this)}
></textarea></p>
<p><input type="submit"></input></p>
</form>
</article>
);
}
}
export default UpdateContent;
결과는 동일~
- 이번엔 .bind(this)
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
}
this.inputFormHandler = this.inputFormHandler.bind(this);
}
inputFormHandler(e){
this.setState({
//title:e.target.value
[e.target.name]:e.target.value
});
}
render(){
console.log(this.props.data);
console.log('Update render');
return(
<article>
<h2>Update</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"
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;
결과는 동일~
(4) 어디를 업데이트할지, state content에 전달하기 위해, 어느 주소로 전달하는 지를 전달하기위한 히든 form 만들기
UpdateContent.js
- state안에 id추가
- onSubmit()안에도 id 추가하면서 e.target을 this.state로 수정
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({
//title:e.target.value
[e.target.name]:e.target.value
});
}
render(){
console.log(this.props.data);
console.log('Update render');
return(
<article>
<h2>Update</h2>
<form action="/create_process" method="post"
onSubmit={function(e){
e.preventDefault();
this.props.onSubmit(
// e.target.title.value,
// e.target.desc.value
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;
app.js
(5) 업데이트가 끝난 다음에 모드를 update에서 read로 변경 / create도 동일하게 적용
- else if(this.state.mode === 'update'){ ... } 안에 있는
*1) onSubmit( function( ..여기 인자로 id 추가하기 .. ){...} )
*2) update에 불필요한 id+1관련 코드 제거
*3-2) 선택된 콘텐츠의 id와 동일한 주소의 콘텐츠[id]를 찾아 덮어쓰기
import React, {Component} from 'react';
import './App.css';
import TOC from "./component/TOC"
import ReadContent from "./component/Read_Content"
import CreateContent from "./component/Create_Content"
import UpdateContent from "./component/Update_Content"
import Subject from "./component/Subject"
import Control from "./component/Control"
class App extends Component{
constructor(props){
super(props);
this.count_contents_id=3;
this.state={
mode :"create",
welcome : {title:'welcome', desc:'hello react'},
selected_content_id : 1,
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
getReadContent(){
console.log('- getReadContent(선택한 콘텐츠 출력)');
var i =0;
while(i<this.state.content.length){
var data = this.state.content[i];
if(data.id === this.state.selected_content_id){
return data;
// _title=data.title; // <- _title는 이제 사용하지 않는다.
// _desc=data.desc;
//break;
}
i++;
}
//return data;
}
getContents(){
console.log('- getContents()');
var _title, _desc,_content = null;
if(this.state.mode === 'welcome'){
_title=this.state.welcome.title;
_desc=this.state.welcome.desc;
_content = <ReadContent title={_title} desc={_desc}></ReadContent>
}else if(this.state.mode === 'read'){
var _cdata = this.getReadContent(); // 함수로 바뀐 부분!의 리턴(data)값을 담기위해 변수를 선언한다.
//debugger;
_content = <ReadContent title={_cdata.title} desc={_cdata.desc}></ReadContent> //_cdata._title가 아니다! ㅋ
}else if(this.state.mode === 'create'){
_content = <CreateContent onSubmit={function(_title, _desc){
var _new_content = this.state.content.concat({
id:this.count_contents_id, title:_title, desc:_desc
});
this.setState({
content: _new_content
});
}.bind(this)}></CreateContent>
}else if(this.state.mode === 'update'){
_cdata = this.getReadContent();
_content = <UpdateContent
data={_cdata}
onSubmit={function(_id, _title, _desc){ //1) id추가 받아 온 값 !
// 2) id+1 제거
// 3-1) ?? 왜 바꿔야하지? 그냥 쓰면 안되나?
// var _new_content = this.state.content.concat({
// id:this.count_contents_id, title:_title, desc:_desc
// });
// -> 안돼지.. concat({})여기 안에, ()나, {} 어디에도.. 반복문을 넣을 방법이 없다.
// 3-2) 불변성을 위해 복사한 후 덥어쓰기
var _ucontents = Array.from(this.state.content);
var i =0;
while(i<_ucontents.length){
// OMG!!! '==='과 '='가, 다른 결과가 나온다! *조심조심*
if(_ucontents[i].id ===_id){
_ucontents[i]={id:_id, title:_title, desc:_desc};
break;
}
i++;
}
this.setState({
content: _ucontents
});
}.bind(this)}></UpdateContent>
}
return _content;
}
render(){
console.log('APP 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
data={this.state.content}
onChangePage={function(id){
this.setState({
mode : 'read',
selected_content_id : Number(id)
})
}.bind(this)}
></TOC>
<Control onChangeMode={function(_mode){
this.setState({
mode:_mode
})
}.bind(this)}></Control>
{/* {_content} */}
{this.getContents()}
</div>
);
}
}
export default App;
업데이트 끝. but 기억해야할 점은
create 할때는 var _A = this...concat({...})을 사용했고 update를 할때는 var _B = Array.from(this...)을 사용했다.
(+) create와 update에 아래 내용 추가하면 mode가 "read"로 바뀌어서, 아래 결과처럼 바뀐다.
this.setState({
content: _ucontents,
mode:"read" // <-- 이거 추가 ~
});
2. Delete
1) 일단 delete 버튼이 가르키는 컴포넌트로 간다.
<li>
<input type="button" onClick={function(e){
e.preventDefault();
this.props.onChangeMode("delete");
}.bind(this)} value="delete"></input>
</li>
2) onChangeMode("delete") 함수가 선언된 곳으로 온다.
<Control onChangeMode={function(_mode){
this.setState({
mode:_mode
});
}.bind(this)}></Control>
3) 그동안 Create, Update에 사용되던 코드에 delete에도 사용할 수 있도록 조건문을 추가한다
<Control onChangeMode={function(_mode){
if(this.state.mode==="delete"){ //* D를 위한 코드
if(window.conform("really?")){ //삭제여부 재확인
//** 삭제를 위한 코드
}
}else{ //* C, U를 위한 코드
this.setState({
mode:_mode
});
}
}.bind(this)}></Control>
4) 삭제를 위한 코드
//1. 지우는 콘텐츠가 몇번방인지 알아야 한다
var _contents = Array.from(this.state.content); //대상 배열을 복재한다.
i=0;
while(i<_contents.lenth){ //복재한 배열의 방을 하나씩 열람한다
if(_contents.id=this.state.selected_content_id){ //while로 열람한 방과 사용자가 선택한 방이 같다면
_contents.splite(i,1); //타깃을 찾았으니, 삭제한다.
break; // 볼일 봤으니 while밖으로 나간다
}
i++;
}
this.setState({
content : _contents, // 원본에 복재품을 덮어쓴다.
mode : "welcome" // 할일을 마쳤으니 기본 모드로 전환한다
})
app.js
import React, {Component} from 'react';
import './App.css';
import TOC from "./component/TOC"
import ReadContent from "./component/Read_Content"
import CreateContent from "./component/Create_Content"
import UpdateContent from "./component/Update_Content"
import Subject from "./component/Subject"
import Control from "./component/Control"
class App extends Component{
constructor(props){
super(props);
this.count_contents_id=3;
this.state={
mode :"welcome",
welcome : {title:'welcome', desc:'hello react'},
selected_content_id : 1,
subject:{title:'WEB', sub:'world wide web !'},
content:[
{id:1, title:"HTML", desc:"HTML is HyperText "},
{id:2, title:"CSS", desc:"Css is for design "},
{id:3, title:"JS", desc:"javascript is for intrective "}
]
}
}
getReadContent(){
console.log('- getReadContent(선택한 콘텐츠 출력)');
var i =0;
while(i<this.state.content.length){
var data = this.state.content[i];
if(data.id === this.state.selected_content_id){
return data;
// _title=data.title; // <- _title는 이제 사용하지 않는다.
// _desc=data.desc;
//break;
}
i++;
}
//return data;
}
getContents(){
console.log('- getContents()');
var _title, _desc,_content = null;
if(this.state.mode === 'welcome'){
_title=this.state.welcome.title;
_desc=this.state.welcome.desc;
_content = <ReadContent title={_title} desc={_desc}></ReadContent>
}else if(this.state.mode === 'read'){
var _cdata = this.getReadContent(); // 함수로 바뀐 부분!의 리턴(data)값을 담기위해 변수를 선언한다.
//debugger;
_content = <ReadContent title={_cdata.title} desc={_cdata.desc}></ReadContent> //_cdata._title가 아니다! ㅋ
}else if(this.state.mode === 'create'){
_content = <CreateContent onSubmit={function(_title, _desc){
var _new_content = this.state.content.concat({
id:this.count_contents_id, title:_title, desc:_desc
});
this.setState({
content: _new_content,
mode:"read"
});
}.bind(this)}></CreateContent>
}else if(this.state.mode === 'update'){
_cdata = this.getReadContent();
_content = <UpdateContent
data={_cdata}
onSubmit={function(_id, _title, _desc){
var _ucontents = Array.from(this.state.content);
var i =0;
while(i<_ucontents.length){
if(_ucontents[i].id ===_id){
_ucontents[i]={id:_id, title:_title, desc:_desc};
break;
}
i++;
}
this.setState({
content: _ucontents,
mode:"read"
});
}.bind(this)}></UpdateContent>
}
return _content;
}
render(){
console.log('APP 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
data={this.state.content}
onChangePage={function(id){
this.setState({
mode : 'read',
selected_content_id : Number(id)
})
}.bind(this)}
></TOC>
<Control onChangeMode={function(_mode){
if(_mode==='delete'){
if(window.confirm('really?')){
var _dcontent = Array.from(this.state.content);
var i =0;
while(i<_dcontent.length){
if(_dcontent[i].id === this.state.selected_content_id){
_dcontent.splice(i,1);
break;
}
i++;
}
this.setState({
mode:"welcome",
content:_dcontent
})
}
}else{
this.setState({
mode:_mode
});
}
}.bind(this)}></Control>
{/* {_content} */}
{this.getContents()}
</div>
);
}
}
export default App;
끄읏~ 그리고,
최종.깃헙코드 링크, 담엔 처음부터 깃헙으로 해야지... 이번엔 클라우드 스토리지처럼..써버렸네ㅋ
https://github.com/normalstory/space-r1
'새로워지기 > 서른의 생활코딩' 카테고리의 다른 글
[생활코딩따라가기] 예고 목차 (0) | 2019.06.01 |
---|---|
[생활코딩 따라가기] REACT, 대망의 CRUD~ 중, create (0) | 2019.05.28 |
[생활코딩 따라가기] React 16 event 풀~패키지 챕터 (0) | 2019.05.27 |
[생활코딩 따라가기] React 15 props와 state (0) | 2019.05.26 |
[생활코딩 따라가기] React 12~14 deep dive컴포넌트 (0) | 2019.05.26 |
댓글