Redux Toolkit
포스트
취소

Redux Toolkit

노마드코더 - 초보자를 위한 리덕스 101을 듣고 정리한 내용입니다.

Redux Toolkit이란

  • Redux를 더 간단하게 사용할 수 있게 하는 라이브러리
  • 기존 Redux의 단점들을 보완하였다.

    • store를 설정하는데 너무 복잡하다!
    • 보일러 플레이트 코드(boilerplate code)가 많다!
      • boilerplate code : 최소한의 변경으로 여러곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드 (ex : Creact React App)
    • 패키지를 많이 설치해야 한다!

Redux Toolkit 설치

  • npm : npm install @reduxjs/toolkit
  • yarn : yarn add @reduxjs/toolkit

createAction

  • Redux의 action type과 action creators를 정의할 때 도움을 주는 함수
1
function createAction(type, prepareAction?);
  • 기존 React-Redux를 사용한 코드
1
2
3
4
5
6
7
8
9
10
const ADD = "ADD";
const DELETE = "DELETE";

export const addToDo = (text) => {
  return { type: ADD, text };
};

export const deleteToDo = (id) => {
  return { type: DELETE, id: parseInt(id) };
};
  • createAction()을 사용한 코드
1
2
3
4
import { createAction } from "@reduxjs/toolkit";

const addToDo = createAction("ADD");
const deleteToDo = createAction("DELETE");
  • addToDo는 함수이며, 실행한 결과를 console에 출력해보면 다음과 같다.
  • addToDo에 text를 인자로 전달했기 때문에 addToDo의 payload는 text가 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
const reducer = (state = [], action) => {
  switch (action.type) {
    case addToDo.type:
      // action에 뭘 보내든 간에 payload와 함께 보내진다.
      console.log(action);
      return [{ text: action.payload, id: Date.now() }, ...state];
    case deleteToDo.type:
      console.log(action);
      return state.filter((toDo) => toDo.id !== action.payload);
    default:
      return state;
  }
};

console.log 결과 console.log 결과


createReducer

  • createReducer를 사용하면 state를 mutate하기 쉽게 만들어준다.
    • 기존의 방법은 state를 mutate한게 아니라 새로 state를 생성했다.
    • 첫 번째 인자 : reducer가 처음 호출될 때 사용되는 state의 초기값
    • 두 번째 인자 : builder Callback함수((builder:Builder) => void)

Builder Callback Notation

  • builder 객체를 argument로 사용한다.
  • addCase, addMatcher, addDefaultCase 함수를 제공하며, reducer가 처리할 action을 정의할 수 있다.

이외에도 Map Object를 사용하는 방법이 있다. 코드의 길이는 조금 짧지만, 자바스크립트에서만 동작하며 타입스크립트와 대부분의 IDE에서는 동작하지 않으므로 builder callback을 사용하는 것을 더 권장한다.


  • builder.addCase
    • reducer가 처리할 하나의 case를 추가한다. 반드시 builder.addMatcher나 builder.addDefaultCase 전에 호출해야 한다.
    • 첫 번째 인자 : actionCreator 또는 string으로 된 action
    • 두 번째 인자 : state 변경 관련 로직
1
2
3
4
5
6
7
8
9
10
const reducer = createReducer([], (builder) => {
  builder
    .addCase(addToDo, (state, action) => {
      // return하지 않았다..!
      state.push({ text: action.payload, id: Date.now() });
    })
    .addCase(deleteToDo, (state, action) => {
      return state.filter((toDo) => toDo.id !== action.payload);
    });
});
  • 첫 번째 addCase에서 새로운 state를 생성해 return하지 않고 바로 state를 변경했다.
    → redux toolkit은 immer를 기반으로 돌아가는데, redux toolkit은 내가 state에 뭔가 추가하고 싶다는 걸 알기 때문에 뒤에서 redux toolkit이 return 작업을 한다.
    결과적으로 state가 mutate되는 것이 아니라고 한다..!
    (immer : 현재 상태를 변경하여 다음 불변 상태를 만든다. - https://immerjs.github.io/immer/ )
  • 어떤 걸 return할 때는 반드시 새 state를 만들어서 return해야 한다.

  • builder.addMatcher
    • 인자로 받은 조건에 따라 state 로직을 처리한다.
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
import { createAction, createReducer } from "@reduxjs/toolkit";

const initialState = {};
const resetAction = createAction("reset-tracked-loading-state");

// 인자로 받은 action이 끝에 '/pending'으로 끝난다면 true, 아니면 false를 return
function isPendingAction(action) {
  return action.type.endsWith("/pending");
}

const reducer = createReducer(initialState, (builder) => {
  builder
    // state 초기화
    .addCase(resetAction, () => initialState)
    // addMatcher는 isPendingAction의 return값이 true라면 두 번째 인자로 받은 함수를 실행한다.
    .addMatcher(isPendingAction, (state, action) => {
      state[action.meta.requestId] = "pending";
    })
    // action이 '/rejected'로 끝나는 경우에만 두 번째 인자로 받은 함수 실행
    .addMatcher(
      (action) => action.type.endsWith("/rejected"),
      (state, action) => {
        state[action.meta.requestId] = "rejected";
      }
    )
    // action이 '/fulfilled'로 끝나는 경우에만 두 번째 인자로 받은 함수 실행
    .addMatcher(
      (action) => action.type.endsWith("/fulfilled"),
      (state, action) => {
        state[action.meta.requestId] = "fulfilled";
      }
    );
});

  • builder.addDefaultCase
    • 앞서 실행한 addCase와 addMatcher에 해당하지 않는 action을 실행한다.
    • 인자로 state를 처리할 로직을 가진 함수를 받는다.
1
2
3
4
5
6
7
8
9
10
import { createReducer } from "@reduxjs/toolkit";
const initialState = { otherActions: 0 };
const reducer = createReducer(initialState, (builder) => {
  builder
    // .addCase(...)
    // .addMatcher(...)
    .addDefaultCase((state, action) => {
      state.otherActions++;
    });
});

createSlice

  • action과 reducer를 한 번에 생성할 수 있다!
  • slice : 기능 별 작은 store
  • 객체를 인자로 받으며, 객체 구조는 다음과 같다.
    • name : action 이름을 지정한다. → createSlice가 action 이름을 지정하는데 사용되는 고정값이다.
    • initialState : state 초기값
    • reducers : redux에서 switch문으로 만들었던 case들을 만든다.
      • 객체의 key값은 Redux DevTools 확장 프로그램에서 표시된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const toDos = createSlice({
  name: "toDosReducer",
  initialState: [],
  reducers: {
    add: (state, action) => {
      state.push({ text: action.payload, id: Date.now() });
    }
  },
  remove: (state, action) => {
    return state.filter((toDo) => toDo.id !== action.payload);
  }
});

console.log(toDos.actions);

console.log 결과 이미지 console.log(toDos.actions)

  • toDos의 actions가 reducers에서 선언한 함수들인 것을 확인할 수 있다.

configureStore

  • createSlice로 만든 slice를 모아서 store를 만들 때 사용한다.
  • 객체를 전달해야 한다.
  • 기존 createStore의 지원이 끝나면서 configureStore을 사용하라고 한다.
    • 하나의 reducer에 slice들을 합치지 않아도 된다. (combineReducers 사용 X)
    • thunk가 적용되어있다. (applyMiddleware 사용 X)
    • Redux DevTools를 기본으로 지원한다.
  • 필수값 : reducer (s 안붙는다!)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// store.js
const store = configureStore({ reducer: toDos.reducer });

export const { add, remove } = toDos.actions;

// Home.js
import { add } from "../store";

const dispatch = useDispatch();

const onSubmit = (event) => {
  // ...
  dispatch(add(text));
};
  • 객체 디스트럭쳐링을 사용하지 않는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// store.js
export const toDos = createSlice({
  // ...
});

// Home.js
import { toDos } from "../store";

const dispatch = useDispatch();

const onSubmit = (event) => {
  // ...
  dispatch(toDos.actions.add(text));
};

출처)
노마드코더 - 초보자를 위한 리덕스 101
Redux Toolkit 공식문서
코딩알려주는누나 - 아직도 옛날 리덕스 쓴다고..? 옛날 리덕스를 최신 리덕스 Toolkit으로 바꿔보자!(Youtube)
생활코딩 - Redux toolkit(Youtube)


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