React Redux 기본학습 -3

React Redux 기본학습 -3

생활코딩의 React Redux 강좌를 보면서 학습한 내용 정리용.

생활코딩 - React Redux

Redux 종속성 제거

실습 화면은 Redux를 사용하지 않았던 것과 동일. 0082-001.png

1-1. components/AddNumber.jsx(this.props.onClick 이벤트로 입력받은 number 를 return 함.

1-2. containers/addNumber.jsx(Container component) (1-1의 AddNumber.jsx에서 onClick 이벤트가 발생하면 값을 넘겨받아서 store 에 담는 역할을 함)

2. components/AddNumberRoot.jsx(아무것도 안해도 됨)

3. Root(/app.js) (아무것도 안해도 됨)

4. components/DisplayNumberRoot.jsx (아무것도 안함)

5-1. containers/DisplayNumber.jsx (store 의 데이터가 바뀌었을때 새로 렌더링 하라는걸 통지하는 subscribe 를 여기에서 설정함. 그리고 5-2 가 필요한 값인 1-1에서 입력받았을 number 를 props로 건내줌)

5-2. components/DisplayNumber.jsx (store의 값이 바뀌든 말든 얘는 5-1.이 필요하면 새로 렌더링하고, 5-1이 주는 값을 this.props.number 로 받아서 표시만 함. 본인은 store의 존재도 모름)

1-2, 5-1만 store를 의식해서 처리함. 나머지는 그냥 컴포넌트만 짜서 보여주거나(2,3,4) 이벤트만 발생시켜서 상위 컴포넌트에 보내주거나(1-1) 상위 컴포넌트에서 받은 값을 뿌리기만 함(5-2).

/store.js

import {createStore} from 'redux';

export default createStore(function(state, action){

    if(state === undefined){
        return {number:0};
    }
    if(action.type === 'INCREMENT'){
        // 01. state 를 복제하고, 복제한 state 에서 변경된 값만 갱신해서 return 한다.
        return {...state, number:state.number + action.size}
    }
    return state;
    },
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); // 디버깅용

1-1. /components/AddNumber.jsx

import React, {Component} from 'react';

/*
export default class AddNumber extends Component {
  state = {size:1}
    render() {
      return (
        <div>
          <h1>Add Number</h1>
          <input type="button" value="+" onClick={function(){
            store.dispatch({type:'INCREMENT', size:this.state.size});
          }.bind(this)}></input>
          <input type="text" value={this.state.size} onChange={function(e){
            this.setState({size:Number(e.target.value)});
          }.bind(this)}></input>
        </div>
      )
    }
  }

  위와 같은 component 모양새는 Redux 의 store 에 의존하고 있기 때문에 AddNumber 를 
  다른 곳에서 사용하는 것이 어렵고 재활용성이 가능한 component 는 아닌것이 된다.
  이를 해결할 방법은 랩핑을 하는 것이다.
  AddNumber라는 component를 감싸는 새로운 component를 만든다
  그리고 그 component는 Redux의 store 를 핸들링 하는 component로 하게 한다.
  AddNumber라는 component는 Redux라는게 이 세상에 있다는 것을 모르는(Redux에 종속되지 않는) component로 만든다.

  이런 개념을 
  - presentational component : Redux라는게 이 세상에 있다는 것을 모르는(Redux에 종속되지 않는) component. 
  - container component 컴포넌트 : Redux, store 와 관련된 작업을 실질적으로 처리하는 component.

  Redux에 종속된다고 잘못된 것은 아니다.
  이 녀석이 다른곳에 사용될 가능성이 없다. 귀찮다. -> Redux에 종속되도록 코딩해도 상관없음.
*/
export default class AddNumber extends Component {
  state = {size:1} // AddNumber 내에서만 인식 가능한 데이터
    render() {
      return (
        <div>
          <h1>Add Number</h1>
          <input type="button" value="+" onClick={function(){
            this.props.onClick(this.state.size);
            // this.props.clickEvent(this.state.size); // props 로 상위 컴포넌트에 돌려줄 필드이름을 onClick 또는 clickEvent 같이 알아보기 좋도록 자유롭게 선언 가능하다.
          }.bind(this)}></input>
          <input type="text" value={this.state.size} onChange={function(e){
            this.setState({size:Number(e.target.value)});
          }.bind(this)}></input>
        </div>
      )
    }
  }

1-2. /containers/AddNumber.jsx

/*
 /components/AddNumber.jsx 를 랩핑할 /containers/AddNumber.jsx 를 만든다. 
 왜 만드냐? /components/AddNumber.jsx 의 Redux 종속성을 없애기 위해.
 이 container component는 1대 1 관계가 되어야 하는건 아니고 이 component 하나가 
 여러개의 react component 들을 감싸는 역할을 해도 되고 이 container component는 
 redux store 만 상대를 하는게 아니라 여러가지 비지니스 로직을 처리할 수 도 있다.

/containers/AddNumber.jsx 는 /components/AddNumber.jsx 를 그대로 가져와서
뿌려주는 역할을 한다.

어플과 종속되는 작업은 이놈이 처리하고 기존의 /components/AddNumber.jsx 는 
화면에 무언가를 표시하는 것에 집중시킴.
 */

import React, { Component } from "react";
import AddNumber from "../components/AddNumber";
import store from '../store';

export default class extends Component{
    render(){
        return <AddNumber onClick={function(size){
            store.dispatch({type:'INCREMENT', size:size});
        }.bind(this)}></AddNumber>
    }
    // AddNumber에서 onClick으로 값이 넘어오면 onClick 으로 받고, clickEvent 으로 넘어 오면 clickEvent으로 받는등 이름은 자유롭게 붙이는게 가능하다.
    // render(){
    //     return <AddNumber clickEvent={function(size){
    //         store.dispatch({type:'INCREMENT', size:size});
    //     }.bind(this)}></AddNumber>
    // } 
    
 }

2. /components/AddNumberRoot.jsx

import React, {Component} from 'react';
import AddNumber from "../containers/AddNumber";

export default class AddNumberRoot extends Component{
    render(){
      return (
        <div>
          <h1>Add Number Root</h1>
          <AddNumber></AddNumber>
        </div>
      );
    }
  }

3. /App.js (root)

import React, {Component} from 'react';
import './App.css';
import AddNumberRoot from "./components/AddNumberRoot";
import DisplayNumberRoot from "./components/DisplayNumberRoot";

class App extends Component {
  state = {number:0}
  render(){
    return (
      <div className="App">
        <h1>Root</h1>
        <AddNumberRoot></AddNumberRoot>
        <DisplayNumberRoot></DisplayNumberRoot>
      </div>
    );
  }
}

export default App;

4. /components/DisplayNumberRoot.jsx

import React, {Component} from "react";
import DisplayNumber from "../containers/DisplayNumber";

export default class DisplayNumberRoot extends Component{
    render(){
      return (
        <div>
          <h1>Display Number Root</h1>
          <DisplayNumber></DisplayNumber>
        </div>
      )
    }
  } 

5-1. /containers/DisplayNumber.jsx

import React, { Component } from "react";
import DisplayNumber from "../components/DisplayNumber";
import store from '../store';

// 이름없는 class 로 감싼다.
export default class extends Component{
    state = {number:store.getState().number};

    // store 에 데이터 값이 바뀔 때 마다 새로 화면 그려주기 위해 
    // 생성자에 subscribe(구독=state 변경시에 인지해서 화면 다시 그려주는 놈) 설정.
    // constructor(props){
    //     super(props);
    //     // // store 에 데이터 값이 바뀌었을 때 본인을 다시 render 해라는 뜻.
    //     // store.subscribe(function(){
    //     //     this.setState({number:store.getState().number});
    //     // }.bind(this));
    // }

    componentDidMount(){
        // store 에 데이터 값이 바뀌었을 때 본인을 다시 render 해라는 뜻.
        store.subscribe(function(){
            this.setState({number:store.getState().number});
        }.bind(this));
    }

    render(){
        return <DisplayNumber number={this.state.number}></DisplayNumber>
    }
 }

5-2. /components/DisplayNumber.jsx

import React, {Component} from "react";

export default class DisplayNumber extends Component {
    render() {
      return (
        <div>
          <h1>Display Number</h1>
          <input type="text" value={this.props.number} readOnly></input>
        </div>
      )
    }
  }

Pie's Tech Note

생계형 개발자의 메모장

comments powered by Disqus

    rss facebook twitter github youtube mail spotify instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora