[Redux] Redux(2)- React-Redux

권준혁·2020년 11월 1일
0

Redux

목록 보기
1/3
post-thumbnail

안녕하세요
Redux 두번째 포스팅입니다.

지난 번 포스팅에서는 View를 담당하는 React를 Redux와 결합하기위해서 store객체의 subscribe 메서드를 사용했습니다.
store.subscribe(()=> callback)

store객체의 데이터가 변경되면 subscribe메서드로 등록한 콜백함수가 동작하므로 콜백함수로 컴포넌트가 업데이트 되도록 하면 됐었는데요

위 그림에서 Store --> View(React)의 과정이었습니다.


아무래도 Redux와 React를 결합하기위해 직접 손으로 일일이 subscribe를 이용하는 것보단,
react-redux 처럼 결합시켜주는 라이브러리를 이용하는게 좋습니다.
또, 단순하게 결합만 시켜주는 것이 아니라 다른 장점들도 가지고 있습니다.

아무튼 그래서 오늘은 react-redux 라이브러리를 사용하는 방법에 대해 알아보고, store를 불변객체로 관리하기위한 immer, 그리고 Redux의 로직에 관여할 수 있는 Middleware를 사용하는 방법에 대해 정리해보겠습니다.


React-Redux

React Redux : Quick Start

npm i react-redux

설치를 마쳤다면, ContextAPI를 사용할 때와 마찬가지로 상위컴포넌트에서 하위컴포넌트들을 Provider 컴포넌트로 감싸줍니다. 이 때 속성값으로 store객체를 넣어줍니다.
Provider컴포넌트는 전달받은 Store객체의 subscribe 메서드를 호출해서 액션 처리가 끝났을 때마다 알림을 받습니다. 그 다음 ContextAPI를 사용해서 리덕스의 상탯값을 하위 컴포넌트로 전달합니다.

  • Provider 컴포넌트
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'

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

  • connect() 함수

Redux는 애플리케이션의 전체 상탯값을 하나의 객체로 관리한다고 했습니다. 애플리케이션이 커진다면 당연히 객체의 크기도 커집니다.
객체의 모든 상태값을 각 컴포넌트 마다 모두 전달한다면 비효율적입니다.
connect() 함수는 컴포넌트에 필요한 상탯값과 Dispatch 함수를 속성값으로 전달합니다.

connect( mapStateToProps, mapDispatchToProps )

  • mapStateToProps : Store객체의 전체 상탯값에서 필요한 상탯값을 전달
  • mapDispatchToProps : 필요한 Dispatch함수를 전달

두 인자는 모두 함수입니다.
두 인자를 작성하는 방법을 살펴보겠습니다.


  • connect() 함수
  • mapStateToProps(state, ownProps?)

이 함수의 리턴값이 컴포넌트의 속성으로 전달되게 됩니다.
필요한 값만 전달해야 불필요한 렌더링이 없습니다.

function mapStateToProps(state) {
  const { todos } = state
  return { todoList: todos.allIds }
}
export default connect(mapStateToProps)(TodoList)

이렇게 전달한다면 컴포넌트 내에서 counter라는 이름으로 속성값을 사용할 수 있습니다.

  • 첫 번째 인자 state

state는 Store의 전체 상탯값이며 store.getState() 로 호출해서 확인할 수 있습니다.
subscribe메서드를 호출하고 싶지 않다면 (컴포넌트의 자동업데이트), mapStateToProps메서드 대신에 null 또는 undefined를 넣어주면 됩니다.

export default connect(null, mapDispatchToProps)(Component)
  • 두 번째 인자 props

생략해도 되지만, 부모 컴포넌트에서 전달하는 속성값을 핸들링 하고싶은 경우 선택적으로 사용할 수 있습니다.

// Todo.js
function mapStateToProps(state, ownProps) {
  const { visibilityFilter } = state
  const { id } = ownProps
  const todo = getTodoById(state, id)
  // component receives additionally:
  return { todo, visibilityFilter }
}

// Later, in your application, a parent component renders:
// <ConnectedTodo id={123} />

위 코드에서는 부모 컴포넌트에서 전달한 id를 이용해 getTodoById라는 메서드를 호출하고 todo라는 이름의 속성값을 생성해 전달했습니다.

  • return

리턴값은 객체로 표현됩니다. 공식문서에 보면 Plain Object를 리턴해야 한다고 합니다.

// Plain Object
var plainObj1 = {}; // typeof plainObj1 --> Object
var plainObj2 = {name : "myName"}; // typeof plainObj2 --> Object
var plainObj3 = new Object(); // typeof plainObj3 --> Object

일반적으로 Class같은 것들을 사용해 리턴하지 않기 때문에 return에서 만큼은 크게 주의할 것은 없는 것 같습니다.

  • mapStateToProps 작성시 주의점

mapStateToProps 메서드는 가능한 한 가볍게 작성해야합니다.
또, 순수함수로 작성해야하며 동기적으로 실행되야합니다.
Ajax같은 비동기 통신을 여기에 작성하면 안됩니다.

다음은 connect()함수의 두 번째 인자 mapDispatchToProps입니다.


  • connect() 함수
  • mapDispatchToProps( dispatch, [ownProps])

connect 메서드의 두 번째 인수 함수인 mapDispatchToProps입니다.
리덕스 store의 dispatch를 속성값으로 전달할 때 사용됩니다.
즉 Store의 상태를 변경할 함수를 컴포넌트의 속성값으로 전달해줍니다.

  • Default

connect(null, null)(MyComponent) 처럼 mapDispatchToProps를 지정하지 않는다면 props.dispatch로 dispatch에 접근할 수 있습니다.
앞서 Provider 컴포넌트의 속성값으로 store를 ContextAPI를 통해 전달하고 있기 때문에 기본적인 사용이 가능한 것입니다.

만약 increment()라고 하는 dispatch 함수를 전달하려고 한다면 컴포넌트 내부에서의 접근방법은 props.dispatch(()=>increment())가 될것입니다.

반대로 mapDispatchToProps를 이용한다면 increment라는 dispatcher를 특정해 사용할 수 있으며 props.increment() 로 직접 속성값으로 참조할 수 있게됩니다.

이렇게 캡슐화 과정을 거치면 React에서는 Redux의 존재를 모르고 컴포넌트를 작성할 수 있게 됩니다.

import { connect } from 'react-redux'
import { increment, decrement, reset } from './actionCreators'

// const Component = ...

const mapStateToProps = (state /*, ownProps*/) => {
  return {
    counter: state.counter
  }
}
const mapDispatchToProps = { increment, decrement, reset }

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Component)
  • 사용법

함수로 정의하는 것과 객체로 정의하는 법 두 가지 방법이 있는데 먼저 함수로 정의하는 방법부터 보겠습니다.

  1. 가장 단순한 사용법입니다. action을 바로 Dispatch합니다.
const mapDispatchToProps = dispatch => {
  return {
    // dispatching plain actions
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' }),
    reset: () => dispatch({ type: 'RESET' })
  }
}
  1. Action에 arguments 가 필요한 경우입니다.
const mapDispatchToProps = dispatch => {
  return {
    // explicitly forwarding arguments
    onClick: event => dispatch(trackClick(event)),

    // implicitly forwarding arguments
    onReceiveImpressions: (...impressions) =>
      dispatch(trackImpressions(impressions))
  }
}
  • 객체로 사용하기

아래 코드처럼 객체 형태로 사용할 수도 있습니다.

import {increment, decrement, reset} from "./counterActions";

const actionCreators = {
  increment,
  decrement,
  reset
}

export default connect(mapState, actionCreators)(Counter);
/* or
export default connect(
  mapState, { increment, decrement, reset }
)(Counter);
*/

React-redux에 Hook이 추가됐습니다.

  • useSelector, useDispatch를 이용하면 더 쉽게 사용할 수 있게됐습니다.
profile
웹 프론트엔드, RN앱 개발자입니다.

0개의 댓글