TypeScript : 투두리스트 만들기 (2단계 - Redux Toolkit)

728x90
반응형

 

 

TypeScript : 투두리스트 만들기 (1단계 - React Props)

Lv1 React를 이용한 TodoList를 만듭니다. Todo를 Create, Delete, Update(완료/취소) 가 가능해야 합니다. 이때, useState와 Props만을 사용합니다. Keyword props drilling, state 끌어올리기 1. 프로젝트 생성 : npx create-re

zerotonine2da.tistory.com

Lv2

  • React + redux-toolkit를 이용한 TodoList를 만듭니다.
  • Todo를 Create, Delete, Update(완료/취소)가 가능해야 합니다.
  • Lv1의 코드를 고쳐서 만듭니다.

Keyword

전역 상태 관리, redux

 


1. props drilling과 useState로 관리했던 todos 데이터 -> 리덕스 툴킷 => App.tsx 간단해짐

[App.tsx]

function App() {
    return (
        <StDiv>
            <StHeader>
                <p>My Todo List📝</p>
                <p>React</p>
            </StHeader>
            <StMain>
                <InputForm />
                <Content isDone={false} />
                <Content isDone={true} />
            </StMain>
            <footer></footer>
        </StDiv>
    );
}

 

2. 리덕스 작업

1) yarn 추가

yarn add react-redux @reduxjs/toolkit

 

 

2) redux폴더에 config / modules 파일 생성 

 

config  - [configStore.tsx]

import { configureStore } from '@reduxjs/toolkit';
import todos from '../modules/todoSlice';
import { useDispatch } from 'react-redux';

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

//useSelector state 타입 정의
export type RootState = ReturnType<typeof store.getState>;

//useDispatch
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch: () => AppDispatch = useDispatch;

export default store;

 

store : 전역에서 state 관리해주는 객체 형태의 공간

 

3) 리덕스툴킷에서 제공하는 configureStore 사용

- 타입스크립트 포인트 : useSelector & useDispatch 의 state 타입 정의

 

* js

const data = useSelector((state)=> state.todos)

const dispatch = useDispatch();

 

* ts (타입선택)

const data = useSelector((state:RootState) => state.todos)

const dispatch = useAppDispatch();

 

 

modules  - [todoSlice.tsx]

import { createSlice } from '@reduxjs/toolkit';
import uuid from 'react-uuid';

//interface 사용 --> 가독성과 유지보수에 좋음
export interface Todo {
    id: string;
    title: string;
    content: string;
    isDone: boolean;
}

//초기 상태 정의
const initialState: Todo[] = [
    {
        id: uuid(),
        title: '제목1',
        content: '내용1',
        isDone: false,
    },
    {
        id: uuid(),
        title: '제목2',
        content: '내용2',
        isDone: true,
    },
];

//타입추론으로 타입지정을 안해도 되는 경우가 있음 : action.payload같은 경우
const todoSlice = createSlice({
    name: 'todos',
    initialState,
    reducers: {
        addTodo: (state, action) => {
            state.push(action.payload);
        },
        removeTodo: (state, action) => {
            return state.filter((item) => item.id !== action.payload);
        },
        switchTodo: (state, action) => {
            return state.map((item) => {
                if (item.id === action.payload) {
                    return { ...item, isDone: !item.isDone };
                } else {
                    return item;
                }
            });
        },
    },
});

export const { addTodo, removeTodo, switchTodo } = todoSlice.actions;
export default todoSlice.reducer;
1) interface 사용해서 todo데이터 타입 정의
2) 초기 상태 정의 
3) 타입추론으로 타입지정을 안 해도 되는 경우가 있음 : action.payload 같은경우
-> 나중에 오류 발생 시 수정
 
[Content.tsx]
import React from 'react';
import styled from 'styled-components';
import { useSelector } from 'react-redux';
import { useAppDispatch } from '../redux/config/configStore';
import { RootState } from '../redux/config/configStore';
import { removeTodo, switchTodo } from '../redux/modules/todoSlice';
type Props = {
    isDone: boolean;
};

function Content({ isDone }: Props) {
    const todos = useSelector((state: RootState) => state.todos);
    const dispatch = useAppDispatch();

    return (
        <>
            <StDiv> {isDone ? '✌️Done✌️' : '✍️Working'}</StDiv>
            <StDivLayout>
                {todos
                    .filter((item) => item.isDone === isDone)
                    .map((todo) => {
                        return (
                            <StDivTodo key={todo.id}>
                                <h3>{todo.title}</h3>
                                <p>{todo.content}</p>

                                <StDivBtn>
                                    <DeleteButton
                                        onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
                                            dispatch(removeTodo(todo.id));
                                        }}
                                    >
                                        삭제
                                    </DeleteButton>
                                    <StateButton
                                        onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
                                            dispatch(switchTodo(todo.id));
                                        }}
                                    >
                                        {isDone ? '취소' : '완료'}
                                    </StateButton>
                                </StDivBtn>
                            </StDivTodo>
                        );
                    })}
            </StDivLayout>
        </>
    );
}

 

export default Content;
1) useSelector와 dispatch 설정
 const todos = useSelector((state: RootState) => state.todos);
 const dispatch = useAppDispatch();
 
2) dispatch로 삭제할 데이터의 id 전달하기
<DeleteButton
     onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
     dispatch(removeTodo(todo.id));
      }}
> 삭제 </DeleteButton>

 

 

★ 

참고 자료 : https://redux-toolkit.js.org/tutorials/typescript

 

TypeScript Quick Start | Redux Toolkit

 

redux-toolkit.js.org

반응형