React 20장 - Redux
포스트
취소

React 20장 - Redux

React

  • Redux 개요

Redux 개요

Props drilling

  • props dilling 의 예시
  • 아래와 같은 구조의 React가 있을 때 해당 배치 상태는 다소 비효율적이다.
  • 상태 끌어올리기와 Props 내려주기를 여러 번 거쳐야하며 애플리케이션이 복잡해질수록 데이터 흐름이 복잡해진다.
  • 컴포넌트 구조가 바뀐다면 데이터의 흐름을 바꿔야 할 수도 있다.

react_redux_props_drilling react_redux_props_drilling2

Redux를 사용할 때의 데이터 흐름과 구조

  • Redux는 전역 상태를 관리할 수 있는 저장소인 Store를 제공한다.
  • 상태가 변경되어야 하는 이벤트가 발생하면 변경될 상태에 대한 정보가 담긴 Action 객체가 생성된다.
  • Action 객체는 Dispath 함수의 인자로 전달된다.
  • Dispatch 함수는 Action 객체를 Reducer 함수로 전달해준다.
  • Reducer 함수는 Action 객체의 값을 확인하고, 그 값에 따라 전역 상태 저장소 Store의 상태를 변경한다.
  • 상태가 변경되면 React는 화면을 다시 렌더링 한다.

  • Redux 에서는 Action=>Dispatch=>Reducer=>Store 순으로 데이터가 단방향으로 흐르게 된다.

react_redux react_redux2

예시

App.js

1
2
3
4
5
6
7
8
9
import React from "react";

export default function App() {
  return (
    <div>
      <h1>{`Count: ${1}`}</h1>
    </div>
  );
}

index.js

1
2
3
4
5
6
7
8
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const rootElement = document.getElementById("root");
const root = creatRoot(rootElement);

root.render(<App />);

Store

  • 상태가 관리되는 하나뿐인 저장소의 역할을 한다.
  • Redux앱의 state가 저장되어 있는 공간이다.
  • createStore 메서드를 활용해 Reducer를 연결해서 Store를 생성할 수 있다.
1
2
import { create } from "redux";
const store = createStore(rootReducer);
  • react-redux에서 Provider를 불러온다. (import)
1
import { Provider } from "react-redux";
  • Provider는 store(저장소)를 손 쉽게 사용할 수 있는 컴포넌트이다.
  • legacy_createStore를 redux에서 불러온다.
1
import { legacy_createStore as createStore } from "redux";
  • 해당 컴포넌트를 불러온 다음 store를 사용할 컴포넌트로 감싸준 후 Provider 컴포넌트의 props로 store를 설정해주면 된다.
1
2
3
4
5
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);
  • 변수 storecreateStore메서드를 할당해준다.
    • createStore의 인자로 counterReducer함수를 전달해준다.
1
const store = createStore(counterReducer);

Reducer

  • Reducer는 외부 요인으로 인해 엉뚱한 값으로 상태가 변경되지 않도록 순수함수여야 한다.
  • 우리가 store에 할당한 전달 인자 함수를 작성한다.
    • Reducer 함수는 Dispatch에게서 전달받은 Action 객체의 type값에 따라 상태를 변경시키는 함수이다.
    • 첫 번째 인자에는 기존 state가 들어오게 하고, default value를 설정하지 않으면 undefined가 할당되기 때문에 꼭 설정해주어야 한다.
    • 두 번째 인자에는 action객체가 들어오게 한다.
    • action객체에서 정의한 type에 따라 새로운 state를 리턴한다.
    • 새로운 state가 Store에 저장된다.
    • actionINCREASE이거나 DECREASE,SET_NUMBER인 경우가 아니면 기존 상태 그대로를 리턴한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const counterReducer = (state = count, action) => {
  switch (action.type) {
    case "INCREASE":
      return state + 1;

    case "DECREASE":
      return state - 1;

    case "SET_NUMBER":
      return action.payload;

    default:
      return state;
  }
};

Action

  • 우리가 함수 안에 정의한 case action으로 어떠한 동작을 할 것인지 설정해준다.
  • type은 꼭 지정해 주어야 하며, 해당 동작을 명시해주는 역할을 하기 때문에 대문자와 Snake Case로 작성해야 한다.
  • 필요에 따라 payload를 작성해 구체적인 값을 전달한다.

    1
    2
    3
    
    {type: "INCREASE"}
    
    {type: "SET_NUMBER", payload: 5}
    
  • action은 보통 함수 형태로 만들어 사용하며 이러한 함수를 액션 생성자라고 부른다.

    • 다른 파일에서도 사용하기 위해 export해준다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      const increase = () => {
        return {
          type: "INCREASE",
        };
      };
      
      const setNumber = (num) => {
        return {
          type: "SET_NUMBER",
          payload: num,
        };
      };
      
  • type작성해보기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    export const increase = () => {
        return (
            type: "INCREASE",
        )
    }
    
    export const decrease = () => {
        return (
            type: "DECREASE",
        )
    }
    

Dispatch

  • Reducer로 Action을 전달해주는 함수이다.
  • Dispatch의 전달인자로 Action의 객체가 전달된다.

    1
    2
    3
    4
    5
    6
    7
    
    // Action 객체를 직접 작성하는 경우
    dispatch({ type: "INCREASE" });
    dispatch({ type: "SET_NUMBER", payload: 5 });
    
    // 액션 생성자(Action Creator)를 사용하는 경우
    dispatch(increase());
    dispatch(setNumber(5));
    

Redux Hooks

  • Store, Reducer, Action, Dsipatch 를 구성하는 것을 완료했으면, 이 개념들을 연결시켜주어야 한다.
  • 이때, Redux Hooks를 사용한다.
  • Redux Hooks는 Redux를 사용할 때 활용할 수 있는 Hooks 메서드를 제공한다.
  • 그 중에서 크게 useSelector(),useDispatch()를 기억하면 된다.

useDispatch()

  • useDispatch()는 Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 반환하는 메서드이다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    import { useDispatch } from "react-redux";
    
    const dispatch = useDispatch();
    dispatch(increase());
    console.log(counter); // 2
    
    dispatch(setNumber(5));
    console.log(counter); // 5
    

useSelector()

  • useSelector()는 컴포넌트와 state를 연결하여 Redux의 state에 접근할 수 있게 해주는 메서드이다.

    1
    2
    3
    
    import { useSelector } from "react-redux";
    const counter = useSelector((state) => state);
    console.log(counter); // 1
    
  • App.js 에 useDispatch를 불러온다.
  • index,js에서 increasedecrease 함수를 불러온다.

    1
    2
    
    import { useDispatch } from "react-redux";
    import { increase, decrease } from "./index.js";
    
  • useDispatch의 실행 값을 변수에 저장하여 dispatch함수를 사용한다.
    1
    
    const dispatch = useDispatch();
    
  • 이벤트 핸들러 안에서 dispatch를 통해 action 객체를 Reducer 함수로 전달해준다.

코드

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React from "react";
import { useDispatch } from "react-redux";
import { increase, decrease } from "./index.js";

export default function App() {
  const dispatch = useDispatch();
  const state = useSelector((state) => state);

  const plusNum = () => {
    dispatch(increase());
  };

  const minusNum = () => {
    dispatch(decrease());
  };

  return (
    <div className>
      <h1>{`Count: ${state}`}</h1>
      <div>
        <button onClick={plusNum}>+</button>
        <button onClick={minussNum}>-</button>
      </div>
    </div>
  );
}

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React from "react"
import {createRoot} from "react-dom/client"
import App from "./App.js"
import {Provider} from "react-redux"
import {legacy_createStore as createStore} from "redux"

const rootElement = document.getElementById("root")
const root = createRoot(rootElement)

export const increase = () => {
    return {
        type: "INCREASE"
    }
}

export const decrease = () => {
    return {
        type: "DECREASE"
    }
}

const count = 1;

const counterReducer = (state=count, action) => {
    switch (action.type){
        case "INCREASE":
            return state + 1

        case "DECREASE":
            return state - 1

        case "SET_NUMBER":
            return action.payload

        default:
            return state
    }
}

const store = createStore(counterReducer)

root.render(
    <Provider store={store}>
        <App />
    </Provider>
)asdasd

StackBlitz 작성해보기

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.