React 주요 커맨드 알아보기

$> npm install -g create-react-app

$> cd {application directory}

$> create-react-app .

$> npm run start

$> npm run build

$> npx serve -s build
  • 디펜던시 관리, 디플로이, jslint등 모든 것을 알아서 해준다는게 참 편하다.
  • 스크립트는 package.json에 기재되어 있다.

react라이브러리에서 Component 오브젝트 상속받아 사용해보기 : Component Class

  • component 상속한 후, render함수를 override 한다.

    import React, {Component} from 'react';
    import './App.css';
    
    class Subject extends Component {
      render (){
          return (
              <header>
                  <h1>WEB</h1>
                  world wide web!
              </header>
          );
      }
    }
    
    class App extends Component {
      render (){
          return (
              <Subject></Subject>
          );
      }
    }
    
    export default App;
  • Component를 상속한 클래스를 태그 <> 안에 넣고 객체처럼 사용할 수 있다

  • react문법을 jsx문법이라고 부른다.

  • 계속해서 html을 Component를 이용해 객체화해보자.

    import React, {Component} from 'react';
    import './App.css';
    
    class Subject extends Component {
      render (){
          return (
              <header>
                  <h1>WEB</h1>
                  world wide web!
              </header>
          );
      }
    }
    
    class TOC extends Component {
      render() {
          return (
              <nav>
                  <ul>
                      <li><a href="1.html">HTML</a></li>
                      <li><a href="2.html">CSS</a></li>
                      <li><a href="3.html">JS</a></li>
                  </ul>
              </nav>
          );
      }
    }
    
    class Content extends Component {
      render() {
          return(
              <article>
                  <h2>HTML</h2>
                  HTML is Hyper Text Markup Language
              </article>
          )
      }
    }
    
    class App extends Component {
      render (){
          return (
              <div>
                  <Subject/>
                  <TOC/>
                  <Content/>
              </div>
          );
      }
    }
    
    export default App;
  • 각 태그들이 class에 의해 객체화 된 것을 볼 수 있다.

상위 Component에서 하위 Compoment로 값 넘겨주기 : props

위의 연습코드에서 App 클래스는 Subject클래스를 태그로 받았다.

이때 태그에 파라미터를 입력하고, Subject클래스에서 받을수는 없을까? 그렇게 할수만 잇다면 파라미터에 의해 내용이 변하는 재사용성이 높은 오브젝트를 만들 수 잇을 텐데말이다.

물론 가능하다. 아래 수정 전후의 소스코드를 눈여거 보길 바란다.

참고: react doc- Components and Props

수정전

...

class Subject extends Component {
    render (){
        return (
            <header>
                <h1>WEB</h1>
                world wide web!
            </header>
        );
    }
}

class App extends Component {
    render (){
        return (
            <div>
                <Subject/>
                <TOC/>
                <Content/>
            </div>
        );
    }
}

수정후

class Subject extends Component {
    render (){
        return (
            <header>
                <h1>{this.props.title}</h1>
                {this.props.sub}
            </header>
        );
    }
}

class App extends Component {
    render (){
        return (
            <div>
                <Subject title="WEB" sub="world wide web"/>
                <TOC/>
                <Content/>
            </div>
        );
    }
}

React developer tool

  1. chrome extention for react : React developer tool(https://reactjs.org/community/debugging-tools.html)
    • react 로 작성된 페이지에 대해, html태그만 으로는 파악하기 어려운 react components들을 보여준다. 대.박

Compoent파일로 분리하기

  • componets라는 디렉토리를 src직하에 만든후, class 별로 구분한 각 component 객체들을 파일별로 분리한다.
  • 이때 각 클래스의 export 방법은 다음과 같은 두 가지가 있다.
    1. export default {ClassName} : 디폴트로 export하는 방법으로, 각 모듈에서 하나의 모듈만 default 키워드를 사용할 수 있다.
    2. export {ClassName}

state 개념 살펴보기

  • Component를 다룰 때, 외부적으로는 Props를 사용한다는 것을 위에서 배웠다.
    state란 Component를 움직이게해주는 내부논리이다.

  • 코드상 state는 그저 초기화함수(constructor)에 포함된 프로퍼티로 보일 것이다. 하지만 내부적으로, state라는 이름의 프로퍼티는 특별하게 사용된다. 예를 들어, 생성자 바깥에서 setState과 같은 함수로 state의 내용을 바꿀 수 있다.

    수정전

    class App extends Component {
      render (){
          return (
              <div>
                  <Subject title="WEB" sub="world wide web"/>
                  <TOC/>
                  <Content/>
              </div>
          );
      }
    }

수정후

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            content: {
                title: 'HTML',
                desc: 'HTML is Hyper Text Markup Language'
            }
        }
    }

    render (){
        return (
            <div>
                <Subject title={this.state.subject.title} sub={this.state.subject.sub}/>
                <TOC/>
                <Content title={this.state.content.title} desc={this.state.content.desc}/>
            </div>
        );
    }
}

export default App;
  • render에 하드코딩 된 부분을 state 인스턴트 프로퍼티를 이용해 은닉화했다.
  • jsx문법 내부에 자바스크립트를 사용하기 위해서는 {}중괄호를 사용하면 된다.

동적으로 html component 생성하기

App.js

import React, {Component} from 'react';
import Subject from './components/Subject'
import Content from './components/Content'
import TOC from './components/TOC'
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ],
            content: {
                title: 'HTML',
                desc: 'HTML is Hyper Text Markup Language'
            },
        }
    }

    render (){
        return (
            <div>
                <Subject title={this.state.subject.title} sub={this.state.subject.sub}/>
                <TOC data={this.state.toc}/>
                <Content title={this.state.content.title} desc={this.state.content.desc}/>
            </div>
        );
    }
}

export default App;

TOC.js

import {Component} from "react";
import React from "react";

class TOC extends Component {
    render() {
        const data = this.props.data;

        const list = [];
        data.forEach((obj) => {
            list.push(
                <li><a href={`/content/${obj.id}`}>{obj.title}</a></li>
            )
        });

        return (
            <nav>
                <ul>
                    {list}
                </ul>
            </nav>
        );
    }
}

// export {TOC};
export default TOC;
  • 여러개의 list를 자동으로 생성할때는 아래와 같은 오류가 발생한다.

    Each child in a list should have a unique "key" prop.
  • 이때 각 리스트가 다른 리스트와 구별될 수 있는 식별자를 attribute로 주면 해결 된다.

    TOC.js

    import {Component} from "react";
    import React from "react";
    
    class TOC extends Component {  
    render() {  
    const data = this.props.data;
    
        const list = [];
        data.forEach((obj) => {
            list.push(
                <li key={obj.id}><a href={`/content/${obj.id}`}>{obj.title}</a></li>
                      )
                  });
    
                  return (
                      <nav>
                          <ul>
                              {list}
                          </ul>
                      </nav>
                  );
              }
          }
    
          // export {TOC};  
          export default TOC;

event의 onClick 구현하기

  • 먼저 이벤트의 이해를 쉽게하기 위해, 기존 Subject 클래스의 Component지정을 멈추고, tag를 하드 코딩하여 보도록하자.

app.js

...
    render (){
        let _title, _desc = null;
        if(this.state.mode === 'welcome') {
            _title = this.state.welcome.title;
            _desc = this.state.welcome.desc;
        }else {
            _title = this.state.content.title;
            _desc = this.state.content.desc;
        }

        return (
            <div>
                {/*<Subject title={this.state.subject.title} sub={this.state.subject.sub}/>*/}
                {/* Temporary subject tag */}
                <header>
                    <h1 href="/" onClick={(e) => {
                        alert('hi');
                        e.preventDefault();
                        debugger;
                    }}>{this.state.subject.title}</h1>
                    {this.state.subject.sub}
                </header>
                <TOC data={this.state.toc}/>
                <Content title={_title} desc={_desc}/>
            </div>
        );
    }
...
  • jsx코드 내부에 debugger라는 함수를 넣으면 개발자도구에서 멈춘다.
  • onClick attribute를 활용해 클릭 이벤트를 넣을 수 있다.
  • e는 이벤트가 사용할 수 있는 다양한 프로퍼티를 갖는다.
    • e.preventDefault()는 태그가 갖는 기본적인 움직임을 막는 역할은 한다. 위의 코드에서는 href를 무력화하여 페이지 이동을 막는다.

event에서 state 변경하기

  • 클릭 이벤트에 의해 mode값이 변경되는 코드를 짜보도록하자.
    app.js

    ...
      render (){
          let _title, _desc = null;
          if(this.state.mode === 'welcome') {
              _title = this.state.welcome.title;
              _desc = this.state.welcome.desc;
          }else {
              _title = this.state.content.title;
              _desc = this.state.content.desc;
          }
    
          return (
              <div>
                  {/*<Subject title={this.state.subject.title} sub={this.state.subject.sub}/>*/}
                  {/* Temporary subject tag */}
                  <header>
                      <h1 href="/" onClick={function(e) {
                          alert('hi');
                          e.preventDefault();
                          debugger;
                      }}>{this.state.subject.title}</h1>
                      {this.state.subject.sub}
                  </header>
                  <TOC data={this.state.toc}/>
                  <Content title={_title} desc={_desc}/>
              </div>
          );
      }
    ...
    
  • 위의 코드는 두 가지의 문제점을 가지고 있다.
    • 첫번째 : this.state.mode = 'welcome';에서 this는 클래스의 인스턴스가 아닌 아무것도 아닌 것이다.
      • -> bind()로 해결한다.
    • 두번째 : react는 state를 변경하기 위해 setState라는 함수를 가지고 있으므로, 그 함수를 사용하여야 한다.
      • 이때 포인트는 생성자에 들어있는 state프로퍼티가 단순한 프로퍼티가 아니라는 점이다.
        react내에서 state프로퍼티는 특별한 취급을 받는다. setState를 하면 단순히
        생성자의 값을 바꾸는 것이 아니라 setState가 호출된 후 render들을 다시 호출하여
        새로운 state로 화면을 구성한다. 예에서는, App -> TOC -> Content 의 render함수를
        새로 호출하여 변경된 값으로 새로운 화면을 구성한다.
...
    render (){
        let _title, _desc = null;
        if(this.state.mode === 'welcome') {
            _title = this.state.welcome.title;
            _desc = this.state.welcome.desc;
        }else {
            _title = this.state.content.title;
            _desc = this.state.content.desc;
        }

        return (
            <div>
                {/*<Subject title={this.state.subject.title} sub={this.state.subject.sub}/>*/}
                {/* Temporary subject tag */}
                <header>
                    <h1 href="/" onClick={function(e) {
                        alert('hi');
                        e.preventDefault();
                        this.setStart({
                            mode: 'welcome'
                        });
                        debugger;
                    }.bind(this)}>{this.state.subject.title}</h1>
                    {this.state.subject.sub}
                </header>
                <TOC data={this.state.toc}/>
                <Content title={_title} desc={_desc}/>
            </div>
        );
    }
...

event에서 bind함수 이해하기

  • 아래의 Component를 상속받은 App클래스 내에서 this는 무엇을 가르킬까

App.js

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            ...
        }    
    }

    render (){
        console.log('what is this?', this);

        ...

        return (

            ...
        );
    }
}
  • react에서 Component를 상속받을 경우 this는 App클래스를 가르키고 있다는 것을 알 수 있다.

  • (참고)일반적으로 this는 세가지의 다른 상황에 의해서 바인드 된다.
    this가 바인드되는 세 가지의 경우

    1. 객체 내부의 메서드에 바인딩 되는 경우
    2. 메서드 내부에서 바인딩 되는 경우
    3. 생성자 new로 생성해 그 인스턴스에 바인딩 되는 경우
  • 이때 onClick 내부에 this는 아무것도 가르키지 않는다.

     <h1 href="/" onClick={function(e) {
          alert('hi');
          e.preventDefault();
          this.setState({
              mode: 'welcome'
          });
          debugger;
      }.bind(this)}>{this.state.subject.title}</h1>
    
  • 따라서 상위 블록에 존재하는 this를 가져와 bind를 해줄 수 있다.

event에서 setState함수 이해하기

  • 동적으로 state값을 변경할 경우 this.setState를 사용하여야 한다.
  • 포인트는 생성자에 들어있는 state프로퍼티가 단순한 프로퍼티가 아니라는 점이다.
    react내에서 state프로퍼티는 특별한 취급을 받는다. setState를 하면 단순히
    생성자의 값을 바꾸는 것이 아니라 setState가 호출된 후 render들을 다시 호출하여
    새로운 state로 화면을 구성한다. 예에서는, App -> TOC -> Content 의 render함수를
    새로 호출하여 변경된 값으로 새로운 화면을 구성한다.

component 이벤트 만들기

  • 소스보고 알아서하기 별로 어렵지 않음

App.js

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mode: 'read',
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            welcome: {
                title: 'welcome',
                desc: 'Hello, React'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ],
            content: {
                title: 'HTML',
                desc: 'HTML is Hyper Text Markup Language'
            },
        }
    }

    render (){
        let _title, _desc = null;
        if(this.state.mode === 'welcome') {
            _title = this.state.welcome.title;
            _desc = this.state.welcome.desc;
        }else {
            _title = this.state.content.title;
            _desc = this.state.content.desc;
        }
        console.log('what is this?', this);
        return (

            <div>
                <Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}
                    onChangePage={function() {
                        if(this.state.mode === 'welcome') {
                            this.setState({
                                mode: 'read'
                            })
                        } else {
                            this.setState({
                                mode: 'welcome'
                            })
                        }

                    }.bind(this)}
                />
                <TOC data={this.state.toc}/>
                <Content title={_title} desc={_desc}/>
            </div>
        );
    }
}

Subject.js

import {Component} from "react";
import React from "react";

class Subject extends Component {
    render (){
        return (
            <header>
                <h1 href="/" onClick={function(e) {
                    e.preventDefault();
                    this.props.onChangePage();
                }.bind(this)}>{this.props.title}</h1>
                {this.props.sub}
            </header>
        );
    }
}

export default Subject;

html의 어트리뷰트 값을 받아 서버에서 처리하기 : 클라이언트에서 값을 받아 서버로 전달하기

App.js

import React, {Component} from 'react';
import Subject from './components/Subject'
import Content from './components/Content'
import TOC from './components/TOC'
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mode: 'read',
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            content: {
                title: 'welcome',
                desc: 'Hello, React'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ]
        }
    }

    render (){
        console.log('App');
        return (
            <div>
                <Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}
                />
                <TOC
                    data={this.state.toc}
                    onChangePage={function(id) {
                        debugger;
                        for (var key of this.state.toc) {
                            debugger;
                            if (key.id === id) {
                                this.setState({
                                    content: {
                                        title: key.title,
                                        desc: key.desc
                                    }
                                });
                                break;
                            }

                        }
                    }.bind(this)}
                />
                <Content
                    title={this.state.content.title}
                    desc={this.state.content.desc}
                />
            </div>
        );
    }
}

export default App;

TOC.js

import {Component} from "react";
import React from "react";

class TOC extends Component {
    render() {
        console.log('TOC');
        const data = this.props.data;

        const list = [];
        data.forEach((obj) => {
            list.push(
                <li key={obj.id}
                    onClick={function(e) { // Event Listener
                        debugger;
                        e.preventDefault();
                        this.props.onChangePage(obj.id);
                    }.bind(this)}>
                    <a href={`/content/${obj.id}`}>{obj.title}</a>
                </li>
            )
        });

        return (
            <nav>
                <ul>
                    {list}
                </ul>
            </nav>
        );
    }
}

// export {TOC};
export default TOC;

이벤트 setState함수 이해하기

  • 상위 컴퍼넌트 (APP)가 하위 컴퍼턴트(TOC, Subject, Content)를 조작할 때는 props 사용
  • 컴퍼넌트가 자기 자신의 상태를 바꿀 때는 state
  • 하위 컴퍼턴트(TOC, Subject, Content)가 상위 컴퍼넌트 (APP)를 조작할 때는 event 사용
  • (props vs state 이미지)

create 구현, 소개 : 각 버튼 클릭시 mode 변경하기

App.js

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

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mode: 'read',
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            content: {
                title: 'welcome',
                desc: 'Hello, React'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ]
        }
    }

    render (){
        console.log('App');
        return (
            <div>
                <Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}
                />
                <TOC
                    data={this.state.toc}
                    onChangePage={function(id) {
                        debugger;
                        for (var key of this.state.toc) {
                            debugger;
                            if (key.id === id) {
                                this.setState({
                                    content: {
                                        title: key.title,
                                        desc: key.desc
                                    }
                                });
                                break;
                            }

                        }
                    }.bind(this)}
                />
                <Control
                    onChangeMode={function(mode){
                        this.setState({
                           mode: mode
                        });
                    }.bind(this)}
                />
                <Content
                    title={this.state.content.title}
                    desc={this.state.content.desc}
                />
            </div>
        );
    }
}

export default App;

Control.js : 새로 생성된 파일

import {Component} from "react";
import React from "react";

class Control extends Component {


    render (){
        // 함수내의 this는 전역 windows를 가르키기 때문에 Control 클래스의 this로 바인드해주어야함
        const onClick = function(e, mode){
                e.preventDefault();
                this.props.onChangeMode(mode)
            }.bind(this);

        return (
            <div>
                <button href="/create" id="create" onClick={function(e){onClick(e, 'create')}}>create</button>
                <button href="/update" id="update" onClick={function(e){onClick(e, 'update')}}>update</button>
                <button id="delete" onClick={function(e){onClick(e, 'delete')}}>delete</button>
            </div>
        );
    }
}

export default Control;

동적으로 컴포넌트 바꿔넣기

App.js

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

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mode: 'read',
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            content: {
                title: 'welcome',
                desc: 'Hello, React'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ]
        }
    }

    render (){
        // Change Component according to mode
        let _article = null;
        if(this.state.mode === 'read') {
            _article = <ReadContent
                title={this.state.content.title}
                desc={this.state.content.desc}
            />
        } else if(this.state.mode === 'create') {
            _article = <CreateContent
                title={this.state.content.title}
                desc={this.state.content.desc}
            />
        }

        return (

            <div>
                <Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}
                />
                <TOC
                    data={this.state.toc}
                    onChangePage={function(id) {
                        for (var key of this.state.toc) {
                            if (key.id === id) {
                                this.setState({
                                    mode: 'read',
                                    content: {
                                        title: key.title,
                                        desc: key.desc
                                    }
                                });
                                break;
                            }

                        }
                    }.bind(this)}
                />
                <Control
                    onChangeMode={function(mode){
                        this.setState({
                           mode: mode
                        });
                    }.bind(this)}
                />
                { _article }
            </div>
        );
    }
}

export default App;
  • _article변수에 mode에 따라 Component객체가 바뀔 수 있도록 설정해줍니다.
    • 위의 코드에서는 mode에따라 ReadContent, CreateContent가 동적으로 _article에 담깁니다.
  • TOC component 내부에 mode: 'read'를 추가하여 list를 클릭했을 때 mode가 read로 바뀌도록 합니다.
  • 재밌는건 { _article } 부분인데, 변수에 Component객체를 넣고 그 변수를 사용할 수 있다는 점입니다.

input의 값을 추출해 서버로 가져오기

CreateContents.js

import {Component} from "react";
import React from "react";

class CreateContent extends Component {
    render() {
        console.log('CreateContent');
        return(
            <article>
                <h2>Create</h2>
                <form action="/create" method="post"
                      onSubmit={function(e) {
                            e.preventDefault();
                            debugger;
                            let title = e.target.title.value;
                            let desc = e.target.desc.value;
                            this.props.onSubmit(title, desc);
                            alert('submit');
                        }.bind(this)}>

                    <p>
                        <input type="text" name="title" placeholder="title"/>
                    </p>
                    <p>
                        <textarea name="desc" placeholder="description"/>
                    </p>
                    <p>
                        <input type="submit"/>
                    </p>

                </form>
            </article>
        )
    }
}

export default CreateContent;
  • html의 값들은 e를 경유해서 받을 수 있다. 크롬 개발자 모드에서 e를 탐색해 보면
    많은 값들이 들어있는데, form값은 target 변수 안에 들어있다.
  • 이때 중요한 것은 e는 각 태그의 name attribute 값을 보고 있는 것이다.
  • 첫번째 input 태그의 name은 title 이였기 때문에 e.target.title.value으로 그 값을 추출할 수 있다.

App.js

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mode: 'read',
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            content: {
                title: 'welcome',
                desc: 'Hello, React'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ]
        }
    }

    render (){
        // Change Component according to mode
        let _article = null;
        if(this.state.mode === 'read') {
            _article = <ReadContent
                title={this.state.content.title}
                desc={this.state.content.desc}
            />
        } else if(this.state.mode === 'create') {
            _article = <CreateContent
                title={this.state.content.title}
                desc={this.state.content.desc}
                onSubmit={function(title, desc) {
                    let lToc = this.state.toc;

                    let addedConent = lToc.concat(
                        {
                            id: (lToc.length)+1,
                            title: title,
                            desc: desc
                        }
                    );

                    this.setState({
                            toc: addedConent
                        }
                    );
                    console.log(this.state.toc);
                    this.forceUpdate();
                }.bind(this)
                }
            />
        }

        return
        (
            <div>
                <Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}
                />
                <TOC
                    data={this.state.toc}
                    onChangePage={function(id) {
                        for (var key of this.state.toc) {
                            if (key.id === id) {
                                this.setState({
                                    mode: 'read',
                                    content: {
                                        title: key.title,
                                        desc: key.desc
                                    }
                                });
                                break;
                            }

                        }
                    }.bind(this)}
                />
                <Control
                    onChangeMode={function(mode){
                        this.setState({
                           mode: mode
                        });
                    }.bind(this)}
                />
                { _article }
            </div>
        );
    }
}

export default App;
  • 여기서 this.setState의 또다른 특징을 알 수 있다.

     this.setState({
             toc: addedConent
         }
     );
    • setState로 toc에 새로운 값을 넣어줬음에도 불구하고, console.log(this.state.toc);
      변화되기 전의 toc 값을 나타내고 있다. 이는 state가 불변성을 유지하기 때문이다.
    • 따라서 toc값을 변화시킬때, push와 같은 본 객체의 불변성을 훼손시키는 값으로 객체를 변화시키는 것보다
      새로운 독립된 객체를 만들어서 setState로 값을 전달하는 것을 추천한다.

shouldComponentUpdate 함수

  • shouldComponentUpdate함수의 특징
    1. render 이전에 shouldComponentUpdate함수가 실행된다.
    2. shouldComponentUpdate함수의 return값이 true이면 render가 호출되고,
      false이면 render는 호출되지 않는다.
    3. shouldComponentUpdate의 인수를 통해,
      새롭게 업데이트된 props의 값과 초기props의 값에 접근할 수 있다.

TOC.js

import {Component} from "react";
import React from "react";

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

    render() {
        console.log('TOC');
        const data = this.props.data;

        const list = [];
        data.forEach((obj) => {
            list.push(
                <li key={obj.id}
                    onClick={function(e) { // Event Listener
                        e.preventDefault();
                        this.props.onChangePage(obj.id);
                    }.bind(this)}>
                    <a href={`/content/${obj.id}`}>{obj.title}</a>
                </li>
            )
        });

        return (
            <nav>
                <ul>
                    {list}
                </ul>
            </nav>
        );
    }
}
// export {TOC};
export default TOC;
  • 앞에서 본바에 의하면 setState로 인해 state값에 변화가 인지될시
    모든 render값이 재호출 되게 된다. 하지만 이것은 값이 변화하지 않은 Component에 대해서도
    render을 호출하기 때문에 매우 비효율적이다.
    • shouldComponentUpdate의 3번의 특징을 이용해 값이 변화하지 않은 Component값에
      대해서는 render가 시행되지 않도록 하게 컨트롤할 수 있다.
  • state값의 원본을 초기값을 유지하지 않으면 위와같은 컨트롤이 불가능하므로
    state값은 초기값을 불변값으로써 유지하는 것이 중요하다.

immutable 불변성

  • 불변성을 유지할 수 있도록 값을 복사해주는 함수들

    • 배열 : Array.from(a)

      const a = [1,2,3,4];
      const b = Array.from(a);
      console.log(b, a===b)
      // Array from은 배열값을 복사하지만 단순히 a를 참조하는 참조값이 아닌 deepCopy이다.
    • 객체 : Object.assign({}, a)

      const a = {a:1, b:2};
      const b = Object.assign({}, a)
      console.log(b, a===b)
      //  Object.assign는 객체값을 복사하지만 단순히 a를 참조하는 참조값이 아닌 deepCopy이다.

update기능 추가하기

app.js

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

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            mode: 'read',
            subject: {
                title: 'WEB',
                sub: 'world wide web'
            },
            content: {
                id: 0,
                title: 'welcome',
                desc: 'Hello, React'
            },
            toc: [
                {
                    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: 'Javascript is for control'
                }
            ]
        }
    }

    getContent() {
        // Change Component according to mode
        let _article = null;
        if(this.state.mode === 'read') {
            _article = <ReadContent
                title={this.state.content.title}
                desc={this.state.content.desc}
            />
        }else if(this.state.mode === 'create') {
            _article = <CreateContent
                onSubmit={function(title, desc) {
                    let lToc = this.state.toc;

                    let addedConent = lToc.concat(
                        {
                            id: (lToc.length)+1,
                            title: title,
                            desc: desc
                        }
                    );

                    this.setState({
                            toc: addedConent
                        }
                    );
                    console.log(this.state.toc);
                }.bind(this)
                }
            />
        }else if(this.state.mode === 'update') {
            _article = <UpdateContent
                title={this.state.content.title}
                desc={this.state.content.desc}
                id={this.state.content.id}

                onSubmit={function(id, title, desc) {
                    const toc = Array.from(this.state.toc);
                    console.log(typeof(toc));
                    toc.forEach(function(obj) {
                        if (obj.id === id) {
                            obj.title = title;
                            obj.desc = desc;
                            return;
                        }
                    });

                    const content = Array.from(this.state.content);
                    content.id = id;
                    content.title = title;
                    content.desc = desc;

                    this.setState({
                        mode: 'read',
                        content: content,
                        toc: toc
                    });
                }.bind(this)
                }
            />

        }

        return _article;
    }

    render() {
        return(
            <div>
                <Subject
                    title={this.state.subject.title}
                    sub={this.state.subject.sub}
                />
                <TOC
                    data={this.state.toc}
                    onChangePage={function(id) {
                        for (var key of this.state.toc) {
                            if (key.id === id) {
                                this.setState({
                                    mode: 'read',
                                    content: {
                                        id: id,
                                        title: key.title,
                                        desc: key.desc
                                    }
                                });
                                break;
                            }
                        }
                    }.bind(this)}
                />
                <Control
                    onChangeMode={function(mode){
                        this.setState({
                            mode: mode
                        });
                    }.bind(this)}
                />
                { this.getContent() }
            </div>
        );
    }
}

export default App;
  • update후에는 바뀐 내용을 content에 덮어씌우고 setState로 re-rendering한다.

UpdateContent.js

import {Component} from "react";
import React from "react";

class UpdateContent extends Component {
    render() {
        console.log('CreateContent');
        return(
            <article>
                <h2>Update</h2>
                <form action="/create" method="post"
                      onSubmit={function(e) {
                            e.preventDefault();
                            debugger;
                            let id = this.props.id;
                            let title = e.target.title.value;
                            let desc = e.target.desc.value;
                            this.props.onSubmit(id, title, desc);
                            alert('submit');
                        }.bind(this)}>

                    <p>
                        <input type="text" name="title" placeholder="title" defaultValue={this.props.title}/>
                    </p>
                    <p>
                        <textarea name="desc" placeholder="description" defaultValue={this.props.desc}/>
                    </p>
                    <p>
                        <input type="submit"/>
                    </p>

                </form>
            </article>
        )
    }
}

export default UpdateContent;

https://medium.com/@TechMagic/reactjs-vs-angular5-vs-vue-js-what-to-choose-in-2018-b91e028fa91d

React vs Angular vs Vue.js — What to choose in 2019? (updated)

TechMagic
TechMagic
Mar 16, 2018 · 5 min read

Some time ago we published an article with a comparison of Angular and React. In that article, we showed the pros and cons of these frameworks and suggested what to choose in 2018 for particular purposes. So, what is the situation in the frontend garden in 2019?

JavaScript frameworks are developing at an extremely fast pace, meaning that today we have frequently updated versions of Angular, React and another player on this market — Vue.js.

We analyzed the number of open positions worldwide that require a specific knowledge of a certain framework. As a source, we took Indeed.com and got the following distribution according to more than 60,000 job offers.

Taking into account the following data, we decided to share the main advantages and disadvantages of every frontend framework and help tech professionals or engineers to choose the best one for their development needs.

Pros and cons of Angular

Angular is a superheroic JavaScript MVVM framework, founded in 2009, which is awesome for building highly interactive web applications.

Benefits of Angular:

  • Angular’s created to be used alongside with Typescript. And has exceptional support for it.
  • Angular-language-service — which allows intelligence and autocomplete inside of component external HTML template files.
  • New features like a generation of Angular based npm libraries from CLI, generation, and development of WebComponents based on Angular.
  • Detailed documentation that allows getting the all necessary information for the individual developer without asking his colleagues. However, this requires more time for education.
  • One-way data binding that enables singular behaviour for the app which minimized risks of possible errors.
  • MVVM (Model-View-ViewModel) that allows developers to work separately on the same app section using the same set of data.
  • Dependency injection of the features related to the components with modules and modularity in general.
  • Structure and architecture specifically created for great project scalability

Drawbacks of Angular:

  • Variety of different structures(Injectables, Components, Pipes, Modules etc.) makes it a bit harder to learn in comparison with React and Vue.js, which have an only “Component” in mind.
  • Relatively slower performance, according to different benchmarks. On the other hand, it can be easily tackled by utilizing so-called “ChangeDetectionStrategy”, which helps to control the rendering process of components manually.

Companies that use Angular: Companies that use Angular: Microsoft, Autodesk, MacDonald’s, UPS, Cisco Solution Partner Program, AT&T, Apple, Adobe, GoPro, ProtonMail, Clarity Design System, Upwork, Freelancer, Udemy, YouTube, Paypal, Nike, Google, Telegram, Weather, iStockphoto, AWS, Crunchbase.

Pros and cons of React

React is a JavaScript library, open sourced by Facebook in 2013, which is great for building modern single-page applications of any size and scale.

Benefits of React:

  • Easy to learn, thanks to its simple design, use of JSX (an HTML-like syntax) for templating, and highly detailed documentation.
  • Developers spend more time writing modern JavaScript, and less time worrying about the framework-specific code.
  • Extremely fast, courtesy of React’s Virtual DOM implementation and various rendering optimizations.
  • Great support for server-side rendering, making it a powerful framework for content-focused applications.
  • First-class Progressive Web App (PWA) support, thanks to the `create-react-app` application generator.
  • Data-binding is one-way, meaning less unwanted side effects.
  • Redux, the most popular framework for managing application state in React, is easy to learn and master.
  • React implements Functional Programming (FP) concepts, creating easy-to-test and highly reusable code.
  • Applications can be made type-safe with either Microsoft’s TypeScript or Facebook’s Flow, with both featuring native support for JSX.
  • Migrating between versions is generally very easy, with Facebook providing “codemods” to automate much of the process.
  • Skills learned in React can be applied (often directly) to React Native development.

Drawbacks of React:

  • React is unopinionated and leaves developers to make choices about the best way to develop. This can be tackled by strong project leadership and good processes.
  • The community is divided on the best way to write CSS in React, split between traditional stylesheets (CSS Modules) and CSS-in-JS (i.e. Emotion and Styled Components).
  • React is moving away from class-based components, which may be a barrier for developers more comfortable with Object Oriented Programming (OOP).
  • Mixing templating with logic (JSX) can be confusing for some developers at first.

Companies that use React: Facebook, Instagram, Netflix, New York Times, Yahoo, Khan Academy, Whatsapp, Codecademy, Dropbox, Airbnb, Asana, Atlassian, Intercom, Microsoft, Slack, Storybook, and many more.

Pros and cons of Vue.js

Vue.js is a JavaScript framework, launched in 2013, which perfectly fits for creating highly adaptable user interfaces and sophisticated Single-page applications.

Benefits of Vue.js:

  • Empowered HTML. This means that Vue.js has many similar characteristics with Angular and this can help to optimize HTML blocks handling with the use of different components.
  • Detailed documentation. Vue.js has very circumstantial documentation which can fasten learning curve for developers and save a lot of time to develop an app using only the basic knowledge of HTML and JavaScript.
  • Adaptability. It provides a rapid switching period from other frameworks to Vue.js because of the similarity with Angular and React in terms of design and architecture.
  • Awesome integration. Vue.js can be used for both building single-page applications and more difficult web interfaces of apps. The main thing is that smaller interactive parts can be easily integrated into the existing infrastructure with no negative effect on the entire system.
  • Large scaling. Vue.js can help to develop pretty large reusable templates that can be made with no extra time allocated for that according to its simple structure.
  • Tiny size. Vue.js can weight around 20KB keeping its speed and flexibility that allows reaching much better performance in comparison to other frameworks.

Drawbacks of Vue.js:

  • Lack of resources. Vue.js still has a pretty small market share in comparison with React or Angular, which means that knowledge sharing in this framework is still in the beginning phase.
  • Risk of over flexibility. Sometimes, Vue.js might have issues while integrating into huge projects and there is still no experience with possible solutions, but they will definitely come soon.

Companies that use Vue.js: Xiaomi, Alibaba, WizzAir, EuroNews, Grammarly, Gitlab and Laracasts, Adobe, Behance, Codeship, Reuters.

CONCLUSION

For a real engineer, there is no substantial difference in which framework to choose, because it just takes some time to get used to the new one. In our company, we grow expertise in mostly React and Angular, but Vue.js is also on board. Every framework has its own pros and cons, meaning that there should be just the right choice for every single case during product development.

'frameworks > react' 카테고리의 다른 글

How the useEffect Hook Works  (0) 2020.03.28
Using the Effect Hook  (0) 2020.03.28
Using the State Hook  (0) 2020.03.28
Redux도입시 디렉토리 구조의 3가지 베스트 프렉티스 패턴  (0) 2020.03.16
React tutorial 강의 요약  (0) 2020.02.12

+ Recent posts