Typescript

[타입스크립트] redux-toolkit 사용법(todoList 예시)

ejunyang 2024. 7. 1. 13:14

 

1. 라이브러리 설치

yarn add react-redux
yarn add @reduxjs/toolkit

 

 

2. Redux Store 설정

📁 redux / 📁 store / 📑 store.ts

타입스크립트로 만든 프로젝트라 RootState를 추가해주었다 RootState타입이 스토어의 상태를 나타내는 타입으로 정의된다.

import { configureStore } from "@reduxjs/toolkit";
import todoSlice from "../slices/todoSlice";

export const store = configureStore({
  reducer: {
    todos: todoSlice,
  },
});

export type RootState = ReturnType<typeof store.getState>;

 

 

3.Slice 생성 및 액션 추가

📁 redux / 📁 slices / 📑 todoSlice.ts

todolist에 들어갈 내용은 id, content, isDone(완료여부)로 구성하였다. 따로 타입 파일을 만들어 인터페이스로 정의해주었다. 

import { createSlice } from "@reduxjs/toolkit";
import { TodosState } from "../../types/TodoType";

const initialState: TodosState = {
  todos: [],
};

const todoSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {
    addTodo: (state, action) => {
      state.todos = [...state.todos, action.payload];
    },
    deleteTodo: (state, action) => {
      state.todos = state.todos.filter((todo) => todo.id !== action.payload);
    },
    toggleTodo: (state, action) => {
      const todo = state.todos.find((todo) => todo.id === action.payload);
      todo ? (todo.isDone = !todo.isDone) : null;
    },
  },
});

export default todoSlice.reducer;
export const { addTodo, deleteTodo, toggleTodo } = todoSlice.actions;

 

3-1. 타입 정의

📁 types / 📑 TodoType.ts

export interface Todo {
  id: number;
  content: string;
  isDone: boolean;
}

export interface TodosState {
  todos: Todo[];
}

 

 

4. Store 통합

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { Provider } from "react-redux";
import { store } from "./redux/store/store.ts";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

 

 

5. 화면에 보여주기

import { useDispatch, useSelector } from "react-redux";
import { RootState } from "./redux/store/store";
import { useState } from "react";
import { addTodo, deleteTodo, toggleTodo } from "./redux/slices/todoSlice";

const App: React.FC = () => {
  const [content, setContent] = useState<string>("");
  const todos = useSelector((state: RootState) => state.todos.todos);
  const dispatch = useDispatch();

  const handleAdd = (e: React.MouseEvent<HTMLFormElement, MouseEvent>) => {
    e.preventDefault();
    const newTodo = {
      id: Date.now(),
      content,
      isDone: false,
    };
    dispatch(addTodo(newTodo));
    setContent("");
  };
  return (
    <>
      <form onSubmit={handleAdd}>
        <input
          type="text"
          value={content}
          onChange={(e) => setContent(e.target.value)}
        />
        <button type="submit">추가</button>
      </form>
      <ul>
        {todos.map((todo) => {
          return (
            <li key={todo.id}>
              <h2>{todo.content}</h2>
              <button onClick={() => dispatch(toggleTodo(todo.id))}>
                {todo.isDone ? "취소하기" : "완료하기"}
              </button>
              <button onClick={() => dispatch(deleteTodo(todo.id))}>
                삭제
              </button>
            </li>
          );
        })}
      </ul>
    </>
  );
};

export default App;