ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [생활코딩] create 구현 : mode 전환 기능 / form / onSubmit 이벤트 /contents 변경 / shouldComponentUpdate / Array.from / Object.assign
    REACT 2022. 2. 1. 16:59

     

    생활코딩 https://www.youtube.com/watch?v=f8e0YWiM-50 

     

    기존 읽기 component 인 Content 에서 create 클릭 시 CreateContent 로 변경시키기

    (그리고 기존 읽기 component 이름을 ReadContent로 변경시키기)

     

    기존 Content.js 를 ReadContent.js 로 변경하기

    import React,{Component} from 'react';
    
    
    class ReadContent extends Component{
        render(){
            console.log('content render');
          return(
            <article>
                  <h2>{this.props.title}</h2>
                  {this.props.desc}
              </article>
          );
        }
      }
    
      export default ReadContent;

     

    CreateContent.js 생성하기

    import React,{Component} from 'react';
    
    
    class CreateContent extends Component{
        render(){
            console.log('content render');
          return(
            <article>
                  <h2>Create</h2>
                  <form>
    
                  </form>
            </article>
          );
        }
      }
    
      export default CreateContent;

     

    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> //mode가 welcome인 경우 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> //mode가 read인 경우 ReadContent
        }else if(this.state.mode==='create'){
          _article=<CreateContent></CreateContent> //mode가 create인 경우 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>
        );
      }

     

     

    CreateContent.js 에서 form 구현하기

    submit 되더라도 action 에 있는 경로로 이동되지 않고 onSubmit 에 있는 이벤트가 실행되도록 한다.

    import React,{Component} from 'react';
    
    class CreateContent extends Component{
        render(){
            console.log('content 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;

     

     

     

    form에 내용 입력 후 submit 버튼을 누르면 App component 에서 contents 에 데이터를 추가되도록 해보자

    submit 버튼을 눌렀을 때 CreateContent 에 이벤트로 설치된 함수를 실행시킨다.

    일단 입력한 내용을 어떻게 출력해야할지 확인해보자.

     

     

     

    debugger을 통해 원하는 data를 찾아본다.

     

    App.js 에서 create 를 클릭했을 때 입력한 내용이 console에 잘 찍히는지 확인해보자.

    else if(this.state.mode==='create'){
          _article=<CreateContent onSubmit={function(_title,_desc){
            //add content to this.state.contents
            console.log(_title,_desc);
          }.bind(this)}></CreateContent>
        }

    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;

     

     

     

    이제 입력한 내용이 App component에 contents 에 data가 추가되도록 해보자

    id 값을 +1 해야한다.

    this.max_content_id = 3;

    여기서 3은 마지막 contents id 값과 같아야한다. 그리고 max_content_id를 state 값으로 하지 않고 객체의 값으로 한 이유는 max_content_id 라는 것은 어떠한 데이터를 추가할 때 id 값을 뭐라고 할지 사용할 정보일 뿐이기 때문이다. ui에 영향을 주지 않기 때문에 state 값으로 안해도 된다.

     

    그리고 create 가 클릭될 때 마다 this.max_content_id 값을 +1 시키는 코드를 CreateContent function에 넣는다.

    그리고 push 보다는 concat 함수를 사용하여 기존 데이터를 변경시키지 않는다.

    그리고 this.sstate.contents.concat(); 처럼 state 값을 직접 수정하면 react 가 알아차리지 못하니 setState를 사용하여 contents 값을 변경시킨다.

    import React,{Component} from 'react';
    import TOC from "./components/TOC";
    import ReadContent from './components/ReadContent';
    import Subject from './components/Subject';
    import Control from './components/Control';
    import CreateContent from './components/CreateContent';
    import './App.css';
    
    
    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 HyperText ...'},
            {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;

     

     

    contents가 변경이 되었다면 글 목록을 표시하는 TOC component render 가 호출되어야 하고,

    단순 조회와 같이 contents가 변경되지 않았다면 TOC component render 는 호출되지 않는 것이 좋다.

    그래서 component 의 render 함수를 실행 시킬지 안시킬지 shouldComponentUpdate 함수를 이용하여 정할 수 있다.

    shouldComponentUpdate 함수는 두개의 매개변수를 갖도록 약속되어 있다.

    shouldComponentUpdate (newProps, newState)

    newProps는 component의 props가 바뀌었을 때 바뀐 props 값

    newState는 component의 state가 바뀌었을 때 바뀐 state 값

     

    아래 코드로 작성하게 되면 변경된 값과 원래의 값이 같다면 render 함수로 넘어가지 않고 종료된다.

    만약 값이 변경되어 원래 값과 변경된 값이 다르다면 return true로 넘어가 render 함수가 실행된다.

    import React, {Component} from 'react';
    
    class TOC extends Component{
        shouldComponentUpdate(newProps,newState){
            console.log('==>TOC render shouldComponentUpdate'
                ,newProps.data
                ,this.props.data
            )
            if(this.props.data===newProps.data){
                return false;
            }        
            return true;
        }
        render(){
            console.log('==>TOC render')
            var lists =[];
            var data = this.props.data;
            var i =0;
            while(i < data.length){
                lists.push(
                    <li key={data[i].id}>
                        <a 
                            href={"/content/"+data[i].id}
                            data-id={data[i].id}
                            onClick={function(e){
                                e.preventDefault();
                                this.props.onChangePage(e.target.dataset.id);
                            }.bind(this)}
                        >{data[i].title}</a>
                    </li>)
                i=i+1;
            }
            return(
                <nav>
                  <ul>
                      {lists}
                  </ul>
                </nav>
            );
        }
      }
      export default TOC;

     

     

    그리고 concat 을 사용하여 create를 실행시켰을 때에는 이러한 결과가 나온다.

    첫번째 인자는 새로운 props이고 원소가 4개인 배열이고, 두번째 인자는 이전 값인 props가 3개인 배열이다.

    만약 concat이 아니라 push를 사용하여 create를 실행시킨다면 이러한 결과가 나온다.

     

     

     

    만약 기존 값을 변경하지 않고 push를 하고싶다면 Array.from API를 사용하여 배열을 복제한 후 push 하면 된다.

    import React,{Component} from 'react';
    import TOC from "./components/TOC";
    import ReadContent from './components/ReadContent';
    import Subject from './components/Subject';
    import Control from './components/Control';
    import CreateContent from './components/CreateContent';
    import './App.css';
    
    
    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 HyperText ...'},
            {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;
            
            let newContents = Array.from(this.state.contents);
            newContents.push(
              {id:this.max_content_id, title:_title, desc:_desc}
            )
            this.setState({
              contents:newContents
            })
            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;

     

     

     

    Array.from 은 배열인 경우에만 사용 가능하고

    객체인 경우에는 Object.assign 을 사용하면 된다.

     

     

    이렇게 여러가지를 외우며 쓰기에는 힘들 수 있다. immutable 라이브러리를 사용한다면 무조건 immutable 즉, 모든 명령어들이 원본에 대해 불변하다. 

    728x90
Designed by Tistory.