[TIL] React 초급: CRUD - Create 구현하기

minami·2021년 5월 31일
0

React

목록 보기
5/12

10. create구현

  • CRUD 중 C, U, D 구현
    • create, update, delete라는 이름의 리스트를 만들어서 클릭하면 App.jsstatemode값을 변경하여 각 기능을 실행할 수 있도록 구현한다.

10-1. mode변경 기능

  • 컴포넌트 하나를 새로 생성하여 이벤트 동작을 통해 mode를 변경할 수 있도록 한다.

    import { Component } from 'react';
    
    class Control extends Component {
        render() {
            console.log('Subject 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 onChangeMode={function(_mode) {
            this.setState({
                mode: _mode
            })
        }.bind(this)}>
    </Control>

10-2. mode 전환 기능

  • 리스트를 클릭할 때 이벤트 동작으로 바뀐 mode마다 다른 컨텐츠 내용이 표시될 수 있도록 한다.

    render() {
        console.log('App render');
        // 컨텐츠 영역에 표시할 _article도 정의해준다.
        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') {
          // while을 이용한 id값 통제
          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}
                onChangePage={function() {
                  this.setState({
                    mode: 'welcome'
                  })
                }.bind(this)}>
              </Subject>
              <TOC
                onChangePage={function(id) {
                  this.setState({
                    // 기본모드는 read로 해두고, string형식인 id값을 숫자형식으로 변경해준다.
                    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 컴포넌트를 생성해서 제목과 내용을 입력할 수 있는 Form을 만든다.

    • Form은 기본적인 생성방식과 똑같이 생성
    • onSubmit 이벤트를 사용해서 양식을 제출했을 때 페이지가 새로고침되지 않도록 preventDefault()함수를 적용하고 props값으로 제목과 내용을 전달할 수 있도록 한다.
    import { 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
                    );
                    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;

10-3. contents 변경

  • 배열을 이용하기 위한 준비: contents 배열의 마지막 id값을 변수로 지정한다.

    constructor(props) {
        super(props);
        // contents의 마지막 id값을 변수로 지정
        this.max_content_id = 3;
        this.state = {
          mode: 'create',
          // 기본으로 지정하는 id값
          selected_content_id: 1,
          welcome: {title:'Welcome', desc:'Hello, React!'},
          subject:{title:'WEB', sub:'World Wide Web'},
          contents:[
            {id:1, title:'HTML', desc:'HTML is HyperText Markup Language'},
            {id:2, title:'CSS', desc:'CSS is for design'},
            {id:3, title:'JavaScript', desc:'JavaScript is for interactive'}
          ]
        }
      }
  • 원본 배열을 직접 변경하는 경우

    push를 이용해서 원본 배열을 직접 변경하는 경우에는 react의 성능 개선(유지보수)에 좋지 않다.

    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;;
            this.state.contents.push(
              { id: this.max_content_id, title: _title, desc: _desc });
            this.setState({
              contents: this.state.contentss
            })
            console.log(_title, _desc);
          }.bind(this)}></CreateContent>
        }
  • concat()을 이용하는 경우

    원본 배열을 변경하지 않으므로 권장하는 방법

    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;;
            // push()를 사용한 원본 배열 변경은 권장하지 않음.
            // 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>
        }

📌 push()와 concat()의 차이

  • push(): 원본 배열에 데이터값을 추가

    var arr = [1,2]
    arr.push(3)
    arr
    > (3) [1, 2, 3]
  • concat(): 원본 배열에 데이터값 추가하지 않고, 변수를 사용하여 새로운 변수에 변경된 데이터값을 저장할 수 있음

    var arr2 = [1,2]
    arr2.concat(3)
    arr2
    > (2) [1, 2]
    
    var result = arr2.concat(3)
    result
    > (3) [1, 2, 3]

10-4. shouldComponentUpdate()

  • 불필요하게 화면이 렌더링되어 성능 저하를 일으키지 않게 하기 위해 사용하는 함수

    • 리액트에서는 부모 컴포넌트가 렌더링될 때마다 자식 컴포넌트도 렌더링된다.

    • 예) 각 리스트를 클릭할 때마다 TOC컴포넌트가 불필요하게 렌더링되는 경우

      image-20210531203505124

  • 특징

    • newPropsnewState라는 2개의 매개변수를 가진다.
    shouldComponentUpdate(newProps, newState) {
        console.log('=====>TOC render shouldComponentUpdate'
                   ,newProps.data
                   ,this.props.data);
        return false;
    }
    =====>TOC render shouldComponentUpdate
    // 새로운 props값 출력
    (4) [{…}, {…}, {…}, {…}]
    0: {id: 1, title: "HTML", desc: "HTML is HyperText Markup Language"}
    1: {id: 2, title: "CSS", desc: "CSS is for design"}
    2: {id: 3, title: "JavaScript", desc: "JavaScript is for interactive"}
    3: {id: 4, title: "React", desc: "React is for UI"}
    length: 4
    __proto__: Array(0)
    
    // 기존의 props값 출력
    (3) [{…}, {…}, {…}]
    0: {id: 1, title: "HTML", desc: "HTML is HyperText Markup Language"}
    1: {id: 2, title: "CSS", desc: "CSS is for design"}
    2: {id: 3, title: "JavaScript", desc: "JavaScript is for interactive"}
    length: 3
    __proto__: Array(0)
  • return값이 true일 때 render()함수를 호출하고, false일 때는 render()함수를 호출하지 않는다.

    shouldComponentUpdate(newProps, newState) {
          console.log('=====>TOC render shouldComponentUpdate'
            ,newProps.data
            ,this.props.data
          );
          if(this.props.data === newProps.data) {
            return false;
          }
          return true;
    }
    // props값이 바뀌지 않았을 때
    App render
    TOC.js:5 =====>TOC render shouldComponentUpdate (3) [{…}, {…}, {…}] undefined
    Control.js:5 Subject render
    App.js:29 App render
    TOC.js:5 =====>TOC render shouldComponentUpdate (3) [{…}, {…}, {…}] undefined
    Control.js:5 Subject render
    App.js:29 App render
    TOC.js:5 =====>TOC render shouldComponentUpdate (3) [{…}, {…}, {…}] undefined
    Control.js:5 Subject render
    App.js:29 App render
    TOC.js:5 =====>TOC render shouldComponentUpdate (3) [{…}, {…}, {…}] undefined
    Control.js:5 Subject render
    
    // props값이 바뀌었을 때
    App.js:62 React React is for UI
    App.js:29 App render
    // render()함수 호출
    =====>TOC render shouldComponentUpdate (4) [{…}, {…}, {…}, {…}] (3) [{…}, {…}, {…}]
    TOC.js:15 =====>TOC render
    Control.js:5 Subject render

📌 이때 push()를 사용하면 원본인 this.props.data가 바뀌기 때문에 shouldComponentUpdate()함수의 if문이 작동해서 데이터가 바뀌었음에도 TOC컴포넌트는 새로 렌더링이 되지 않는 문제가 발생한다. 따라서 원본 데이터를 바꾸지 않는 concat() 함수를 사용하는 것을 권장한다.

10-5. immutable

리액트의 불변성

리액트의 컴포넌트는 불변성을 가져야 한다.

  • 불변성의 장점
    1. 함수에서 side effect가 발생할 확률이 줄어든다.
    2. 동기화 문제에서 자유롭다.
    3. 컴포넌트 최적화가 가능하다.

📌 리액트의 불변성을 위한 자바스크립트 라이브러리인 immutable.js 사용에 대해 알아보기

profile
함께 나아가는 개발자💪

0개의 댓글