dwook.record
Published on

Redux-Toolkit

Authors
  • avatar
    Name
    dwook

Table of Contents

설정

yarn add @reduxjs/toolkit
store/todo.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { TodoType } from "../types/todo";

interface TodoReduxState {
  todos: TodoType[];
}

//* 초기 상태
const initialState: TodoReduxState = {
  todos: [],
};

const todo = createSlice({
  name: "todo",
  initialState,
  reducers: {
    //* 투두 변경하기
    setTodo(state, action: PayloadAction<TodoType[]>) {
      state.todos = action.payload;
    },
  },
});

// 다른 파일에서 디스패치할 때 사용하도록 export
export const todoActions = { ...todo.actions };

export default todo;
  • redux-toolkit에서는 (1)액션타입 정의, (2)액션 생성자 정의하는 과정이 없어짐.
  • createSlice가 리턴한 객체
    • actions: 액션 생성자들. todo.actions
    • caseReducers
    • name
    • reducer: combineReducers에 todo.reducer로 전달
  • namer과 reducers의 속성이름으로 액션타입이 만들어짐. todo/setTodo
store/index.ts
import {
  TypedUseSelectorHook,
  useSelector as useReduxSelector,
} from "react-redux";
import { HYDRATE, createWrapper } from "next-redux-wrapper";
import { configureStore, combineReducers } from "@reduxjs/toolkit";
import todo from "./todo";

const rootReducer = combineReducers({
  todo: todo.reducer,
});

const reducer = (state, action) => {
if (action.type === HYDRATE) { const nextState = { ...state, ...action.payload, }; return nextState; } return rootReducer(state, action); }; // * 스토어의 타입: 스토어의 타입을 rootReducer로 부터 얻는다. export type RootState = ReturnType<typeof rootReducer>; // * 타입 지원되는 커스텀 useSelector 만들기 export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector; const initStore = () => { return configureStore({ reducer, devTools: true, }); }; // App 컴퍼넌트에서 wrapper로 사용하기 위해 'next-redux-wrapper'에서 createWrapper를 import export const wrapper = createWrapper(initStore);
  • redux-toolkit은 데브툴을 포함하고 있어서 configureStore에서 devTools: true 만으로 사용 가능
pages/index.tsx
import React from "react";
import { NextPage } from "next";
import TodoList from "../components/TodoList";
import { getTodosAPI } from "../lib/api/todo";
import { wrapper } from "../store";
import { todoActions } from "../store/todo";

const app: NextPage = () => {
  return <TodoList />;
};
export const getServerSideProps = wrapper.getServerSideProps(
  async ({ store }) => {
    try {
      const { data } = await getTodosAPI();
      store.dispatch(todoActions.setTodo(data));
return { props: {} };
} catch (e) { console.log(e); return { props: {} }; } } ); export default app;
  • 리덕스에 데이터를 저장하였기 때문에 페이지에 props를 전달하지 않아도 된다.

useSelector

  • useSelector로 리덕스 스토어의 값을 가져옴.
  • useSelector의 state 타입은 알 수 없으므로 RootState 타입을 가져와서 지정.
  • 매번 RootState를 불러와서 타입을 지정하는 것은 번거로움.

1. useSelector에 타입을 지원하지 않을 경우

components/TodoList.tsx
import { useSelector } from "react-redux";
import { RootState } from "../store";

const TodoList:React.FC = () => {
    const todos = useSelector((state: RootState) => state.todo.todos);
}

2. useSelector에 타입을 지원하는 경우

  • 방법1. 커스텀 useSelector를 만들면 'react-redux'가 아닌 'store'에서 useSelector를 import하여 사용.
store/index.ts
import {
  TypedUseSelectorHook,
  useSelector as useReduxSelector,
} from "react-redux";

// * 스토어의 타입: 스토어의 타입을 rootReducer로 부터 얻는다.
export type RootState = ReturnType<typeof rootReducer>;

// * 타입 지원되는 커스텀 useSelector 만들기
export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector;
  • 방법2. RootState에 타입지원
store/index.ts
declare module 'react-redux' {
    interface DefaultRootState extends RootState {}
}
components/TodoList.tsx
import { useSelector } from "../store";

const TodoList:React.FC = () => {
    const todos = useSelector((state) => state.todo.todos);
}

useDispatch

import { useSelector } from "../store";
import { useDispatch } from "react-redux";
import { todoActions } from "../store/todo";

const TodoList: React.FC = () => {
    const todos = useSelector((state) => state.todo.todos);
  const dispatch = useDispatch();

    dispatch(todoActions.setTodo(newTodos));
}

참조링크