programming/React

React. Create 구현

dmchoi 2021. 3. 18. 17:44

Mode 변경

TOC 컴포넌트 아래에 create, update, delete 구현하기

create, update는 눌렀을때 특정 페이지로 이동해서 실행되게 하고 delete는 눌렀을때 페이지 이동 없이 실행되게 함

 

delete에 link를 사용하게 되면, 사용자들이 어떤 페이지를 방문할 때 페이지가 삭제될 수도 있는 큰 문제가 발생하기 때문에 페이지 개념이 아니라 버튼으로 구현

 

components 디렉토리에 Control.js 생성 

 

App.js

 <Control onChangeMode={function(_mode){ //호출 시 mode 값 변경
          this.setState({
            mode:_mode
          });
        }.bind(this)}></Control>
        <Content title={_title} desc={_desc}></Content>

Control.js

import React, { Component } from 'react';

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>
    );
  }
}

export default Control;

Mode 전환

create 클릭했을 때, CreateContent 컴포넌트 생성하기

원래 있던 Content 파일을 ReadContent로 바꾸고 CreateContent 파일 생성

 

App.js

import React, { Component } from 'react';
import './App.css';
import TOC from "./components/TOC";
import CreateContent from "./components/CreateContent"
import ReadContent from "./components/ReadContent";
import Subject from "./components/Subject";
import Control from "./components/Control";


class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      mode: 'welcome',
      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 Hyper Text Markup Language' },
        { id: 2, title: 'CSS', desc: 'CSS is for design' },
        { id: 3, title: 'JavaScript', desc: 'JS 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'){ //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) //TOC.js에서 id값을 가져옴
            });
          }.bind(this)} data={this.state.contents}
        ></TOC>
        <Control onChangeMode={function(_mode){
          this.setState({
            mode:_mode
          });
        }.bind(this)}></Control>
        {_article} // 위에 _article= <ReadContent title={_title} desc={_desc}></ReadContent>
      </div>
    );
  }
}

export default App;

Form 구현

CreateContent.js

class CreateContent extends Component {
    render() {
      return (
        <article>
          <h2>Create</h2>
          <form action="/create_process" method="post" // create_process 페이지로 사용자가 입력한 정보를 전달
          onSubmit={function(e){
            e.preventDefault();
          }.bind(this)}> 
            <p><input type="text" name="title" placeholder="title"></input></p>
            <p>
              <textarea name="desc" placeholder="description"></textarea> //textarea : 사용자가 입력할 텍스트가 여러줄일 때 사용
            </p>
            <p>
              <input type="submit"></input>
            </p>
          </form>  
        </article>
      )
    }
  }

from 태그에선 submit 했을 때, action으로 설정한 페이지로 화면이 이동됨

onSubmit : submit 버튼 클릭하면 설정한 이벤트가 실행 되도록함 -> e.preventDefault로 기본 동작인 페이지가 이동하는걸 막아줌

 

onSubmit 이벤트

submit 버튼을 눌렀을 때, CreateContent에 이벤트로 설치된 함수를 실행시켜 사용자가 입력한 값을 App 컴포넌트의 Contents로 추가하기

 

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 // target이 form을 가리킴
        );
    }.bind(this)}
>

value의 값을 각각 _title, _desc 인자로 전달

 

Content 변경

App 컴포넌트의 Contents에 데이터 추가하기

class App extends Component {
  constructor(props) {
    super(props);
    this.max_content_id = 3; // UI에 아무런 영향을 주지 않기 때문에 state로 안해도 됨
    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을 이용

state에다 값을 추가할때는 push와 같이 원본 데이터를 바꾸는 것을 사용하지 마라

기존에 있던 데이터에 값을 하나씩 추가하는 방식 -> 나중에 성능 개선할때 매우 까다로워짐

 

shouldComponentUpdate

app.js에 contents 값이 바뀔때마다 TOC 컴포넌트의 render 함수가 호출되어 TOC가 다시 랜더링됨

근데 contents가 바뀌지 않을때에도 TOC가 랜더링됨

-> 자기랑 상관없는 일이 진행되는 것은 불합리. 작은 프로그램에서는 상관없는데 큰 프로그램에선 이슈가 될수도

 

TOC.js

class TOC extends Component {
  shouldComponentUpdate(newProps, newState){
    if(this.props.data === newProps.data){
      return false;
    }
    return true;
  }

shouldComponentUpdate() 함수

 

컴포넌트의 render 함수가 실행되지 않을지를 결정할 수 있음

newProps, newState 두 개의 매개변수를 가질 수 있음

newProps.data ->바뀐값, this.props.data ->바뀌기 전의 값

false를 반환하면 render함수가 호출되고 true를 반환하면 render 함수가 호출됨

 

Immutable

원본이 변하지 않는다. 불변성

 

배열

var a = [1, 2];
var b = Array.from(a);
console.log(a, b, a===b);
=> [1, 2] [1, 2] false // 내용이 같을 뿐, 완전 다른 배열임

concat 말고 push를 사용하고 싶다면 Array.from( ) 사용하면 됨

 

객체

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는 내용이 같지만 다른 객체

Object.assign 을 통해서 객체를 복제할 수 있다. 하지만 내용만 같고 다른 객체임

 

'programming > React' 카테고리의 다른 글

useEffect  (0) 2021.08.03
클래스형 컴포넌트 vs 함수형 컴포넌트  (0) 2021.08.02
React. 이벤트  (0) 2021.02.18
React. 컴포넌트 만들기, Props, state  (0) 2021.02.18
React. 리액트 입문  (1) 2021.02.17