[React] 생활코딩-5 Create 구현

ji_silver·2020년 7월 4일
0

[React] 생활코딩

목록 보기
5/6
post-thumbnail

1. mode 변경

  • <TOC><Content> 컴포넌트 사이에 create, update, delete 버튼 만들기
  • components 디렉터리 안에 Control.js 파일 생성

Control.js

class Control extends Component {
    render() {
        return (
            <ul>
                <li><a href="/create" onClick={function(e){  // 클릭 시 onChangeMode 핸들러 실행되게 하는 코드
                    e.preventDefault();
                    this.props.onChangeMode('create'); // 호출 시 mode값 전달하기
                }.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>
        );
    }
}

App.js

import Control from "./components/Control";

<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 state값 바꾸기
        mode: _mode
    });
}.bind(this)}></Control>
<Content title={_title} desc={_desc}></Content>

2. mode 전환

  • create 클릭 시 <CreateContent> 컴포넌트로 바꿔보기
  • 원래 있던 create 파일을 ReadContent로 모두 바꾸고, CreateContent 파일 생성

App.js

import CreateContent from './components/CreateContent';
import UpdateContent from './components/UpdateContent';

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') { // mode가 'create'일 때 CreateContent 나오게 하기
        _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({
                    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}
	    //가변적으로 바꾸기 위해 _article변수 사용
        </div>
    );
}

3. form

CreateContent.js

import React, { Component } from 'react';

class CreateContent extends Component {
    render() {
        return (
            <article>
                <h2>Create</h2>
                <form action="/create_process" method="post"
                    // 사용자가 데이터 추가, 수정 시 method를 post 방식을 써야 url 노출x 
                    onSubmit={function (e) { // Submit 버튼 클릭 시 폼 태그에 포함되어 있는 onSubmit 이벤트 실행
                        e.preventDefault();
                    }.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;

1) onSubmit 이벤트

App.js

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

CreateContent.js

<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>

e.target은 form 자체를 가리킴
value는 입력한 값을 가져올 수 있기 때문에 _title, _desc 인자로 값을 넘겨줌

4. contents 변경

  • onSubmit 이벤트가 실행됐을 때 App컴포넌트의 contents 배열 끝에 데이터 추가하기

App.js

class App extends Component {
  constructor(props) {
    super(props);this.max_content_id = 3; // state값이 아닌 이유는 UI에 아무런 영향을 주지 않기 때문
    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 for information' },
        { id: 2, title: 'CSS', desc: 'CSS is for design' },
        { id: 3, title: 'JavaScript', desc: 'JavaScript is for interactive' }
      ]
    }
  }
  
render() {
...
  else if (this.state.mode === 'create') {
    _article = <CreateContent onSubmit={function (_title, _desc) {
      this.max_content_id = this.max_content_id + 1; // 원래 있던 id값을 1 증가var _contents = this.state.contents.concat(
        { id: this.max_content_id, title: _title, desc: _desc }
      )
      this.setState({
        contents: _contents
      });
    }.bind(this)}></CreateContent>
  }
}  
  
  • push가 아닌 concat으로 배열 추가를 했을까?
    - push는 원본배열 값이 바뀜, concat은 원본을 바꾸지 않고 원본을 변경한 새로운 배열 리턴
var a = [1, 2];
a.push(3);
console.log(a);
[1, 2, 3]
var a = [1, 2];
var b = a.concat(3);
console.log(a, b);
[1, 2] [1, 2, 3]
=> 원본이 바뀌지 않음

1) shouldComponentUpdate

  • App.js에 contents 배열의 값이 바뀐다면 TOC 컴포넌트의 render()를 통해 TOC가 다시 그려짐
    But! contents가 바뀌지 않아도 TOC가 또 랜더링 됨
  • shouldComponentUpdate() 함수는 컴포넌트의 render함수가 실행될지 실행되지 않을지 결정할 수 있음
  • shouldComponentUpdate(newProps, newState)로 사용할 수 있다.

TOC.js

class TOC extends Component {
  shouldComponentUpdate(newProps, newState){ //- TOC 컴포넌트의 Props와 State값이 바뀐 값을 매개변수로 준다
    if(this.props.data === newProps.data){
      return false; // 현재 props와 바뀐 props가 같다면 함수 호출 x -->
    }
    return true; //그렇지 않다면 render() 호출
  }
}  
  • return false 면 render 함수 호출x, true면 render가 호출됨
  • TOC로 들어오는 data의 props가 바뀌었을 때 render호출, 바뀌지 않았을 때 render 호출x

🔥그렇기 때문에 concat을 쓰지 않고 push를 썼다면?
원본이 바뀌어 이전값, 바뀐값이 같아져서 shouldComponentUpdate()를 쓰지 못함.

2) immutable

  • 원본을 바꾸지 않음

<배열>

var a = [1, 2];
var b = Array.from(a);
console.log(a, b, a===b);
=> [1, 2] [1, 2] false

b.push(3);
console.log(a, b, a===b);
=> [1, 2] [1, 2, 3] false

a,b는 내용이 같을 뿐 완전히 다름
  • 만약 concat이 아닌 push를 쓰고 싶다면 Array.from() 사용
var newContents = Array.from(this.state.contenst);
newContents.push({id:this.max_content_id, title:_title, desc:_desc});
this.setState({
  contents: newContents
});

<객체>

  • Object.assign() 첫 번째 인자로 빈객체 또는 새로운 객체 넣을 수 있음
var a = { name: 'egoing' };
var b = Object.assign({}, a);
console.log(a, b, a === b);
=> {name: "egoing"} {name: "egoing"} false

b.name = 'leezche';
console.log(a, b, a === b);
=> {name: "egoing"} {name: "leezche"} false

a, b는 내용이 같지만 같은 객체가 아님
  • immutable.js를 사용하면 모든 명령어들이 원본에 불변하기 때문에 일관된 코드를 사용할 수 있다.
profile
🚧개발중🚧

0개의 댓글