[달력 만들기 toy project] 날짜별 메모 CRUD ① : toDos 상태 추가 → combineReducer 사용.

이민선(Jasmine)·2023년 5월 9일
0
post-thumbnail

이번 시간부터는 날짜별 메모 기능 구현을 시작할 것이다. 특정 날짜를 클릭하면 그 날짜에 해당하는 메모를 추가할 수 있고, 이전에 추가했던 해당 날짜의 메모도 list로 볼 수 있도록 구현하고 싶다.

input과 버튼을 하나 만들어놓고 스따뜨!

지금까지는 연도와 월만 Redux 상태 관리 대상이었지만, 이제부터는 메모 CRUD를 구현해야 하기 때문에 메모의 상태도 Redux로 관리해야 한다.

상태가 2개 이상일 때는 combineReducer를 사용하면 된다고 한다. 그래서 이번에는 새로운 reducer 파일과 기존 reducer를 함께 묶어주기 위해 rootStore 파일을 생성하였다.

기존의 store 파일의 store.tsx는 calendarStore.tsx로 바꿔주었고,
toDoStore.tsx를 새롭게 생성하였다.

toDosStore.tsx

import { PayloadAction, configureStore, createSlice } from "@reduxjs/toolkit";

type ToDo = {
  id: number;
  year: number;
  month: number;
  date: number;
  text: string;
};

const initialToDosState: ToDo[] = [];

export const toDos = createSlice({
  name: "toDosReducer",
  initialState: initialToDosState,
  reducers: {
    addToDo: (state: ToDo[], action: PayloadAction<ToDo>) => {
      return [action.payload, ...state];
    },
    deleteToDo: (state: ToDo[], action: PayloadAction<ToDo>) => {
      return state.filter((toDo) => toDo.id !== action.payload.id);
    },
    updateToDo: (state: ToDo[], action: PayloadAction<ToDo>) => {
      return state.map((toDo) =>
        toDo.id === action.payload.id
          ? { ...toDo, text: action.payload.text }
          : toDo
      );
    },
  },
});

export const toDostore = configureStore({ reducer: toDos.reducer });
export const { addToDo, deleteToDo, updateToDo } = toDos.actions;

toDos의 상태 관리도 역시 나의 사랑 createSlice로 만들어보았다.

이름하여 "toDosReducer"이다.

여기서 처음에 좀 헷갈렸던 부분!! createSlice로 만드는 calendar와 toDos는 sliceReducer라고 불리는 객체이다. 객체 내부에 name, initialState, action 메서드를 정의해서 하나로 모듈화하는 것이다.

그리고 initialState에는 ToDo[]라는 타입을 주었는데,
처음에는 이렇게 주었다가

initialState: [] as ToDo[],

오늘도 역시
type assertion이 권장되지 않는 타입이라고 배웠쥐!!
라고 외치며

const initialToDosState: ToDo[] = [];

export const toDos = createSlice({
  name: "toDosReducer",
  initialState: initialToDosState,
  .
  .

이렇게 바꿔주었다. 아예 ToDo[] 타입이 적용된 변수인 initiaToDoState를 initialState에 할당하는 방법이 있었다. 아주 나이스~

그리고 이번 목표가 CRUD인 만큼 action은 3개를 만들었다.

  • addToDo : 메모 추가
  • deleteToDo : 메모 삭제
  • updateToDo : 메모 수정

호우,,, 나 잘 할 수 있겠지?!!
state의 type주는 건 사실 어렵지 않았다. 위에서 지정한 ToDo[]가 될테니까!
그런데 action의 type 대체 어떻게 줘야해...???
여기서 벌써 헤맸다.
.
.
제네릭이라고 한다..Aㅏ.. 내가 아직 진도 안나가서 몰랐던 바로 그 제네릭이었구나...
제네릭 공부하고 PayloadAction이라는 type은 다시 봐야겠다. 저 <> 내부에 있는 ToDo가 action의 payload type을 나타낸다고 한다. 더 자세히 공부해봐야겠다!!

reducer 함수 내부를 보자면

  • addToDo의 경우에는 spread 연산자를 사용하여 action.payload를 state에 추가하도록 했다.
  • deleteToDo의 경우에는 filter를 사용하여 action.payload.id와 다른 것들만 살아남도록 했다.
  • updateToDo의 경우에는 map을 사용하여 action.payload.id와 같은 원소일 경우 text 속성의 값만 action.payload.text로 변경되도록 코드를 짜보았다.

rootStore.tsx

import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { calendar } from "./calendarStore";
import { toDos } from "./toDosStore";

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

export type RootState = ReturnType<typeof rootReducer>;

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

나 이제 상태 2개나 있는 사람이야~~~ 든든한데 부담스러운 이 기분은 뭐지?? ㅋㅋㅋㅋㅋㅋㅋ

combineReducer 내부에 각 store 파일에서 import 해온 calendar와 toDos의 reducer 속성을 인자로 전달한다.

묶어서 configureStore 내부에 rootReducer를 값으로 가지는 reducer라는 속성을 가지는 객체를 인자로 전달하고, store라는 변수에 저장한다.

이제 App.tsx 등 다른 파일에서 useSelector를 이용하여 state의 속성으로서 각 reducer의 상태에 접근이 가능해진다.

useSelector로 각각의 초기 상태를 console 출력해보자.

  const toDosList = useSelector((state: RootState) => state.toDos);
  console.log("toDosList 초기 상태", toDosList);
  const state = useSelector((store: CalendarProps) => store.calendar);
  console.log("날짜 초기 상태", state);

뷰티풀...
날짜 초기 상태는 현재 연도와 월을 속성으로 가진 객체로 지정해놨고, toDosList는 빈 배열로 정의해 놓았다.

이제 combineReducer로 합쳤으니 CRUD 구현할 준비가 되었다! 되었나...?!

다음시간부터 호기롭게 도전!!!

profile
기록에 진심인 개발자 🌿

0개의 댓글