관리 메뉴

Dev Blog

10. Redux 본문

BootCamp_Codestates/IM Tech Blog

10. Redux

Yongjae Kim 2021. 1. 11. 19:50

 

Achievement goals

  • 상태 관리 라이브러리가 왜 필요한지 이해할 수 있다.
  • Redux (혹은 Flux Pattern)에서 사용하는 Action, Reducer 그리고 Store의 의미와 특징을 이해할 수 있다.
  • Redux의 3가지 원칙이 무엇이며, 주요 개념과 어떻게 연결되는지 이해할 수 있다.
  • Presentational 컴포넌트와 Container 컴포넌트의 개념을 이해할 수 있다.
  • Redux hooks(useSelector, useDispatch)를 사용해 store 를 업데이트할 수 있다.

Redux 란?

상태 관리 라이브러리

목적?

React 특성인 불필요한 렌더링을 보완하기 위함.

 

React 렌더링 조건이라 하면,

1. State 가 변경된 경우

2. State 변경으로 인해 Props 가 변경된 경우

3. 부모 컴포넌트가 렌더링된 경우

 

즉, state 값 변경으로 인해 re-rendering 이 발생하는데

이 때 re-rendering 이 필요없는 컴포넌트(state 값에 영향을 받지 않는) 또한 렌더링되는 성능저하를 초래한다.

프로젝트의 규모가 커질수록 data flow 가 복잡해질 수 있고 버그나 오류로 인해 트래킹이 불가할 수 있다.

 

예) React this.state 사용 & React Hooks 사용

아래와 같은 경우, 어떤 상태가 변하는지에 상관없이 일괄적으로 전체가 re-rendering 되는 것을 확인할 수 있다.

currentVideo 가 변경되었는데, <VideoList/> 도 불필요한 렌더링이 발생하여 프로그램의 성능이 저하될 수 있다.

this.state 로 상태를 저장하는 경우와 Hooks 메소드로 상태를 사용하는 경우

어떻게?

컴포넌트와 상태를 분리 Store 라는 오직 하나의 상태관리 공간을 만들어 상태 변화가 생겼을 때 해당 상태를 사용하는 컴포넌트만 렌더링 시킨다.

 

before)

기존 component 간의 정보 공유. 상태 관리가 매우 복잡하다.
수많은 컴포넌트가 있는 경우

after)

Redux 의 세 가지 원칙

1. Single source of truth : store 에 저장. 상태 관리를 위한 단 하나의 공간.

2. State is read-only: action 으로 state 를 변경한다. redux 가 reducer 를 통해 상태를 변경하고, react 컴포넌트는 해당 데이터를 읽기 전용으로 불러온다.

3. Changes are made with pure functions: reducer 와 관련. pure function => side effect 가 없다는 것.

 

store

 

상태가 관리되는 오직 하나의 공간

action

 

자바스크립트 객체. App 에서 얻는 이 정보(action 객체)를 store 에 운반한다.

type 을 반드시 작성해줘야 하는데, 이 type 에 따라 reducer 가 동작하기 때문이다.

action 객체의 예시

reducer

 

현재 상태와 action 을 이용하여 다음 상태를 만든다.

여기에서 Dispatch 는 메소드. 이 메소드가 reducer 를 호출한다.

reducer 는 action 객체의 type 에 따라 동작을 선택하고, payload(변경된 데이터)를 활용하여 Store 를 업데이트 한다.

 

 

Overview

 

Redux 의 장점

1. 상태를 예측 가능하게 만들어준다.

2. 유지보수가 용이하다.

3. 디버깅에 유리하다. action과 state log를 기록하면 추적이 가능. => redux dev tool 로 확인할 수 있다.

4. 테스트를 붙이기 쉽다.

순수함수를 사용하기 때문에 테스트를 붙이기 쉽다.

5. React 는 UI 만 담당하는 것과 같아지기 때문에 Redux를 통한 데이터 관리가 쉬워진다.

모든 데이터는 Redux 로 관리.

Presentational Component VS Container Component

1) Presentational Component

: Props 에서 data 를 읽고 props 에서 콜백을 호출하는 Redux 와 연관 없는 컴포넌트

ㅇㅇㅇㅇㅇㅇ

2) Container Component 

: Redux 의 State 에 접근하여 데이터(Store)에 영향을 줄 수 있는 컴포넌트로 Redux 와 연관이 있다.

How to approach and update Store

Redux hooks 인 useSelector, useDispatch 를 이용

 

Redux Thunk middleware , 비동기 액션 생산자

allows you to write action creators that return a function instead of an action. The thunk can be used

to delay the dispatch of an action, or to dispatch only if a certain condition is met.

The inner function receives the store methods dispatch and getState as parameters.

 

리덕스에서 비동기 작업을 처리할 때는 redux-thunk라는 미들웨어를 많이 사용합니다.

비동기 액션 생산자는, 상태에 따라 동기 액션 생산자를 호출해줍니다. 여기서 말하는 상태는 비동기 요청 시작/완료/실패 등을 포함할 수 있습니다. 비동기 액션 생산자는 리듀서로 연결되지 않고, 직접 dispatcher를 통해 스토어로 새로운 상태를 보내줍니다.

함수를 dispatch 할 때에는, 해당 함수에서 dispatch 와 getState 를 파라미터로 받아와주어야 합니다.

이 함수를 만들어주는 함수를 우리는 thunk 라고 부릅니다.

 

thunk?

A thunk is a function that wraps an expression to delay its evaluation.

// calculation of 1 + 2 is immediate
// x === 3
let x = 1 + 2;

// calculation of 1 + 2 is delayed
// foo can be called later to perform the calculation
// foo is a thunk!
let foo = () => 1 + 2;

 

1) An action creator that returns a function to perform asynchronous dispatch:

비동기적인 dispatch 를 실행하기 위한 함수를 리턴하는 액션 creator

const INCREMENT_COUNTER = 'INCREMENT_COUNTER';

function increment() {
  return {
    type: INCREMENT_COUNTER,
  };
}

function incrementAsync() {
  return (dispatch) => {
    setTimeout(() => {
      // Yay! Can invoke sync or async actions with `dispatch`
      dispatch(increment());
    }, 1000);
  };
}

이 경우, 동기적으로 dispatch 를 통해 increment() 의 리턴값을 리듀서에 전달하고 1초 뒤(1000) dispatch 를 통해 increment() 의 리턴값을 리듀서에 재 전달한다.

 

2) An action creator that returns a function to perform conditional dispatch:

조건적인 dispatch 를 실행하기 위한 함수를 리턴하는 액션 creator

function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

이 경우, counter 가 짝수인 경우 dispatch 를 통해 increment() 의 리턴값이 리듀서에 전달된다.


Check in Code

const { createStore } = require("redux");

//!초기 state(데이터)를 정의한다.
const initState = {
  name: "김코딩",
  posts: [],
};

//!action creator
//!action 은 객체로 data 를 받으면 type 과 payload(여기에서 data) 가 reducer 에게 전달된다.
const changeUsername = (data) => {
  return {
    type: "CHANGE_NAME",
    data,
  };
};

const addPost = (post) => {
  return {
    type: "ADD_POST",
    post,
  };
};
//! reducer: 실제 메소드로 데이터를 변경해준다. 
//! initState(prevState)와 action 을 받아 type 에 따라 
//! payload를 활용하여 특정 method 를 실행시킨다. 
const reducer = (prevState, action) => {
  switch (action.type) {
    case "CHANGE_NAME":
      return {
        ...prevState,
        name: action.data,
      };
    case "ADD_POST":
      return {
        ...prevState,
        posts: [...prevState.posts, action.post],
      };
    default:
      return prevState;
  }
};

//! Store
//! createStore 메소드를 활용. createStore(reducer, [preloadedState], [enhancer])
//! reducer: returns the next state tree, given the current state tree and an action to handle.
//! [preloadedState]: initState  
//! [enhandcer]: to enhance the store with third-party capabilities such as middleware, time travel, persistence, etc.
const store = createStore(reducer, initState);

//! dispatch
//! The base dispatch function always synchronously sends an action to the store's reducer, 
//! along with the previous state returned by the store, to calculate a new state. 
//! It expects actions to be plain objects ready to be consumed by the reducer.
store.dispatch(changeUsername("김제이"));
store.dispatch(addPost("포스트1"));
store.dispatch(addPost("포스트2"));

console.log(store.getState());

결과값은 다음과 같다.

 

 

'BootCamp_Codestates > IM Tech Blog' 카테고리의 다른 글

13. authentication  (0) 2021.01.20
11. Databases  (0) 2021.01.13
8-1. React 데이터흐름 & State 끌어올리기  (0) 2021.01.05
8. React Part1  (0) 2021.01.01
5. Asynchronous & Promise  (0) 2020.12.21
Comments