ttaeng_99 2021. 2. 16. 18:58
반응형

이전 포스팅에서 React+Redux 조합을 했듯이, Container 와 Presentational 기능분리의 번거로움이 있었다.

아마, 이를 Context API의 Provider, Consumer 개념처럼 Global State를 공급하는 역할을 이 react-redux가 해줄 것 같다.


💜 React? Redux? React-Redux?

React는 자바스크립트 기반 SPA 라이브러리, Redux는 React 뿐만 아니라 JS환경의 상태관리를 개선하기 위해 등장한 라이브러리다.
그렇다면, React 프로젝트 시 설치하는 Redux 말고, React-Redux 라이브러리의 의의는 무엇일까?

- 의의

Redux 자체는 React, Angular, Vue 및 vanila JS 를 포함한 모든 UI 레이어 또는 프레임워크와 함께 사용 가능한 독립형 라이브러리이다.

모든 UI 프레임워크는 Redux를 사용하는 경우, UI 코드에서 'UI 바인딩 라이브러리' 를 사용하여 프레임워크와 Redux를 연결한다.

 

React-Redux 는 React 공식 Redux 바인딩 라이브러리이다. React + Redux를 사용하는 경우, 두 라이브러리를 바인딩해야 한다.

(출처: https://react-redux.js.org/introduction/why-use-react-redux )

 

한마디로, react-redux는 react와 redux를 바인딩(연결) 해주는 라이브러리 인 것이다.

 

- 설치

npm install react-redux

💜 React-Redux 주요 키워드

react-redux 에서 추가적으로 다루는 개념(키워드)들이 있다. 이에 대해 간단하게 정리해보았다.

위에서 언급했듯, React-Redux 가 제공하는 함수들은 대부분 React ~ Redux 간 연결기능을 주로 담당한다.

 

1. Provider

  • Provider는 connect() 함수에 포장된 모든 컴포넌트가 Redux 스토어를 사용할 수 있게 해준다. (props로 스토어를 받음)
  • 이같은 특징으로, 앱 최상위 레벨에서 Provider를 렌더링한다.
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import reducers from "./reducers";
import { Provider } from "react-redux";	  // Provider는 react-redux 라이브러리에서 가져옴
import App from "./components/App";

ReactDOM.render(
  // Provider 사용예시
  <Provider store={createStore(reducers)}>
    <App />
  </Provider>,
  document.querySelector("#root")
);

 

2. connect()

  • connect() 함수는 React 컴포넌트를 Redux 스토어와 연결한다. 
  • connect() 된 컴포넌트는 스토어에서 상태를 받아오거나, 디스패치 전달을 할 수 있다.
  • connect() 문법: connect([mapStateToProps], [mapDispatchToProps])([전달할 컴포넌트명])
import React from 'react'
import { connect } from 'react-redux;   // connect 는 'react-redux' 라이브러리에서 가져옴
class Example extends Component {
  render(){
    return(
    <>
     예시 컴포넌트의 내용
    </>
    )
 } 

export default connect(null)(Example)

 

3. mapStateToProps

  • connect() 함수의 첫 번째 인자로 전달된다.
  • 스토어 내부의 모든 state 값을 인자로 받으며, 컴포넌트가 필요로 하는 데이터 객체를 반환한다.
  • 연결된 컴포넌트에서 필요한 상태(state)를 선택하는데 사용된다. (= 상태 불러오기, getState)
  • store 상태가 바뀔 때마다 호출된다. (= 구독, subscribe)
import React from 'react'
import { connect } from 'react-redux;

class Example extends Component {
  render(){
    return(
    <>
     예시 컴포넌트의 내용
    </>
    )
 }

const mapStateToProps = (state) => {
  return {
    //받아올때 쓸 state의 이름' : state.'reducer에서 정한 데이터 이름' 
    needData : state.needData
  }
}

export default connect(mapStateToProps)(Example)

mapStateToProps() 함수는 해당 컴포넌트에서 스토어의 상태를 받고자 할 때 사용된다. (useContext Hooks 적용과 유사)

스토어의 state를 인자로 받아, 우리가 사용하고자 하는 객체형태로 재구성한다. 이 객체는, Example 컴포넌트의 props로 전달된다.

 

4. mapDispatchToProps

  • connect() 함수의 두 번째 인자로 전달된다.
  • 스토어의 dispatch 함수를 인자로 받으며, 컴포넌트에 적용할 함수를 객체에 담아 전달한다.
import React from 'react'
import { connect } from 'react-redux;

class Example extends Component {
  render(){
    return(
    <Example onClick={this.props.onClick}>
     예시 컴포넌트의 내용
    </Example>
    )
 }

const mapDispatchToProps = (dispatch) => {
  return {
    //받아올때 쓸 props(함수명)의 이름' : dispatch(action) 함수
    onClick : () => {
      dispatch({
        type: 'INCREMENT',
        size: 2,
      })
    }
  }
}

export default connect(null, mapDispatchToProps)(Example)

mapDispatchToProps 역시 객체를 반환하는데, 특징은 키가 props명, 키값은 우리가 활용할 디스패치 함수가 된다.


💜 React-Redux 적용하기

기존에 제작한 Container 컴포넌트들을 각각 React-Redux 를 통해 간소화하는 작업을 해보겠다.

// AddNumberCon.js

import React from 'react'
import AddNumber from '../components/AddNumber';
import store from '../Store';

const AddNumberCon = () => {
  return (
    <AddNumber onClick={(size) => {
      store.dispatch({
        type: 'INCREMENT',
        size,
      })
    }}/>
  )
}
// ShowNumberCon.js

import React, { useState } from 'react';
import store from '../Store';
import ShowNumber from '../components/ShowNumber';

const ShowNumberCon = () => {
  const [number, setNumber] = useState(0);
  store.subscribe(() => {
    setNumber(store.getState().number)
  })

  return (
    <ShowNumber number={number} />
  )
}

 

- React-Redux 적용

1. Provider 설정

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'; 
import store from './Store';
import './index.css';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
  , document.getElementById('root')
);

reportWebVitals();

앱 최상위에서 다음과 같이 <Provider>로 감싸준다. props는 스토어를 지정해주면 된다.

 

 

2. Container 컴포넌트의 connect() 적용

 

1) mapStateToProps : map + Redux-State + to + React-Props

// ShowNumberCon.js

import { connect } from 'react-redux';
import ShowNumber from '../components/ShowNumber';

const mapStateToProps = (state) => {
  return {
    number: state.number,
  }
}

export default connect(mapStateToProps)(ShowNumber);

상태값을 활용하는 <ShowNumber> 컴포넌트의 경우, mapStateToProps 를 통해 스토어의 상태를 전달한다.

스토어 상태 중, 우리가 필요한 number에 대해서만 재가공한 객체가 props로 전달된다.

 

2) mapDispatchToProps : map + Redux-Dispatch + to + React-Props

// AddNumberCon.js

import { connect } from 'react-redux';
import AddNumber from '../components/AddNumber';

const mapDispatchToProps = (dispatch) => {
  return {
    onClick: (size) => {
      dispatch({
        type: 'INCREMENT',
        size,
      })
    }
  }
}

export default connect(null, mapDispatchToProps)(AddNumber);

이벤트 핸들러로 디스패치를 활용하는 <AddNumber> 컴포넌트의 경우, mapDispatchToProps 를 통해 디스패치 함수를 전달한다.

객체 형태로 props가 전달되며, 키는 props명, 키값은 dispatch(action) 함수가 들어간다.


확실히 Context API 에 비해 러닝커브가 높다는 것이 느껴졌다. (useReducer 를 제외한 비교이기는 하나)

무엇보다, Container 컴포넌트가 아직 존재한다는 것에 대해 의문이 들기도 한다. (mapStateToProps, mapDispatchToProps)

 

또한, 생활코딩 강의는 클래스형 컴포넌트 위주로 이루어졌기에, 함수형에 관한 공부도 추가하면서 좀 더 원활하고 효율적으로 Redux를 활용할 수 있는 방법에 대해 고민해봐야겠다.

 

[출처]

- Redux 공식문서: react-redux.js.org/introduction/quick-start  

- mokyungg 님의 블로그: velog.io/@mokyoungg/Redux-react-redux-react-redux

반응형