React

[React] Typescript + React Redux 프로젝트에 적용하기

밍띠이 2024. 4. 19. 18:34
반응형


지난 프로젝트에서 Vite (비트) + Typescript 로 리액트 프로젝트를 진행하며 상태관리 툴로 리액트 리덕스를 사용하였다.

타입스크립트 리액트 프로젝트에서 리덕스를 사용하는 방법을 정리하고

또 언제 리덕스를 사용하면 좋은지 작성해 보고자 한다.

 

지난 프로젝트 폴더 구성이다.

기능별로 store을 나누어 생성했고, index에서 관리하였다.

 

0.  라이브러리 설치

npm install @reduxjs/toolkit react-redux


1. store 생성


store/index.ts

import { configureStore } from "@reduxjs/toolkit";

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

export type RootReducer = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

jsx와 동일하게 configureStore 을 이용하여 생성할 수 있다.


외부 컴포넌트에서 state,dispatch를 사용하기 위해서는 타입 지정 필요

1-1. 훅(Hooks) 타입 지정

import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootReducer } from "../store";

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootReducer> = useSelector;

- TypedUseSelectorHook : 타입 지원

useAppDispatch: 새 커스텀 훅 정의, 액션을 디스패치 할때마다 타입 체크가 가능해져 타입 안정성을 보장할 수 있다.

또, useAppSelector을 새로 정의하면서 타입에 맞게 상태를 선택할 수있어 역시 타입 안정성이 강화된다.

 

 

2. 슬라이스 생성

 

예시로, 채팅 슬라이스의 코드를 가져와보았다.

store/chatPage/chatSlice.ts

// chatSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

interface ChatState {
  chats: string[];
  selectedChatId: string | null;
  selectedChat: string | null;
}

const initialState: ChatState = {
  chats: [],
  selectedChatId: null,
  selectedChat: null,
};

const chatSlice = createSlice({
  name: "chat",
  initialState,
  reducers: {
    setSelectedChatId(state, action: PayloadAction<string | null>) {
      state.selectedChatId = action.payload;
      state.selectedChat =
        state.chats.find(
          (chat, index) => index.toString() === action.payload
        ) || null;
    },
    setChats(state, action: PayloadAction<string[]>) {
      state.chats = action.payload;
    },
  },
});

export const { setSelectedChatId, setChats } = chatSlice.actions;
export default chatSlice.reducer;


state와 action의 타입 정의 및 지정이 필요하다.

각 슬라이스 파일은 초기 상태 값에 대한 유형을 정의해야 createSlice 가 가능하다.

따라서 인터페이스로 타입 정의를 먼저 해주고,

initialState에서 초기값을 설정해 주어야 한다.

PlayloadAction<T> 로 'action.playload'의 타입을 정의하여야 한다.

 

셀렉터를 사용하려면 아래와 같이 store 파일 유형으로 가져와야 한다.

import type { RootState } from './store'

export const selectCategory = (state: RootState) => state.chat.value

 

3. store에 리듀서 추가

store/index.ts

import { configureStore } from "@reduxjs/toolkit";
import chatReducer from "./chatPage/chatSlice.ts";

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

export type RootReducer = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

 

사용할 리듀서 들을 작성해 준다.


4. 컴포넌트에서 store의 state와 action 접근하기


pages/ChatPage/Chat.tsx

import React, { useEffect } from 'react'
import { useAppSelector, useAppDispatch } from "../../../hooks/redux";
import { setSelectedChatId, setChats } from "../../../store/chatPage/chatSlice";


const Chat = () => {
	const selectedChatId = useAppSelector(
    (state) => state.chatReducer.selectedChatId
  );
    const dispatch = useAppDispatch();

	// ...
    
    useEffect(() => {
   		dispatch(setChats(data));
    });
}

 

미리 구성한 hooks에서 selector와 dispatch 함수를 가져와 이용할 수 있고,

store 에 저장된 값을 꺼내 쓰거나, 액션 함수를 사용 가능하다.

 

 

이번 프로젝트에서 openvidu 라는 오픈 소스 라이브러리를 jsx -> tsx 로 마이그레이션 하여 적용하며

타입과 그로인한 리덕스 적용에 많은 문제를 만났었지만,

오히려 덕분에 한번 더 개념을 정리할 수 있어서 도움이 되었던 것 같다.

반응형