TypeScript - TypeScript + Redux

꼬디바아 ㅣ 2024. 3. 19. 15:14

728x90

TypeScript 대표이미지

 

🖥️ Redux 란?

2024.02.19 - [👨‍💻 React] - Redux 이전 글을 참고하자

 

⌨️ Redux Toolkit 및 react-redux 설치

// npm
npm install @reduxjs/toolkit react-redux

// yarn
yarn add @reduxjs/toolkit react-redux

 

Redux를 카운터 예제로 알아보자.

 

import React from 'react';

const Counter: React.FC = () => {
  const counter = 0;

  return (
    <div>
      <h2>{counter}</h2>
      <div>
        <button>더하기</button>
        <button>빼기</button>
      </div>
    </div>
  );
};

export default Counter;

 

버튼을 눌러서 counter를 조작하는 컴포넌트가 있다. 지금은 버튼을 눌러도 작동하지 않을 것이다.

 

1. store 만들기

const store = configureStore({
  reducer: {리듀서 map},
});

 

타입스크립트에서도 store는 위와 같이 configureStore를 사용해서 만들 수 있다. 단, 외부 컴포넌트에서 store의 state와 dispatch를 사용하기 위해서는 타입을 지정해줘야 한다.

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

 

  • RootState : Redux 스토어의 state를 나타내는 타입
  • AppDispatch : Redux 액션을 dispatch하는 함수의 타입
import { configureStore } from '@reduxjs/toolkit';

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

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

export default store;

 

2. 슬라이스 만들기

const slice = createSlice({
  name: slice이름,
  initialState: state초기값,
  reducers: {
    // 리듀서구현
  },
});

 

 - state, action의 타입 정의하기

interface StateType {
  // state 타입 정의
}
interface ActionType {
  // action 타입 정의
}

 

- 슬라이스에 정의한 타입 지정해주기

initialState와 action에 타입을 지정해준다. 단, action의 타입은 PayloadAcition<액션타입>으로 지정해야 한다.

const initialState: StateType = 초기값
// 리듀서 함수 내부
someReducer: (state, action: PayloadAction<ActionType>) => {
  // code...
 },
import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';

interface CounterState {
  counter: number;
}

const initialState: CounterState = {
  counter: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    add: (state, action: PayloadAction<number>) => {
      state.counter += action.payload;
    },
    sub: (state, action: PayloadAction<number>) => {
      state.counter -= action.payload;
    },
  },
});

export const counterActions = counterSlice.actions;
export const default counterSlice.reducer;

 

- payloadAction<number>는 dispatch 해오는 함수의 파라미터 타입을 number로 지정한다.

 

 

비워두었던 store의 redcer에 counterSlice에서 정의한 리듀서 함수를 등록하면 된다.

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counter';

const store = configureStore({
  reducer: {
    counter: counterReducer, // 추가됨
  },
});

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

export default store;

 

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

JavaScript를 사용한 Redux의 경우 useDispatch와 useSelector를 사용해 컴포넌트에서 스토어에 접근했다.

 

하지만 TypeScript에서는 새롭게 정의한 타입을 사용해야한다.

 

useDispatch는 함수 형태이고, 리턴 타입은 리덕스 스토어의 dispatch 이다. 이는 이미 stroe/index.ts에서 AppDispatch 타입으로 정의한 타입이기 때문에 AppDispatch를 import 해서 사용한다.

 

export const useAppDispatch: () => AppDispatch = useDispatch;

 

useSelector는 useSelectorHook 타입이지만, 우리가 정의한 RootState로 제너링 타입을 지정해야 한다.

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

 

hook/index.ts

import { useDispatch, useSelector } from 'react-redux';
import type { TypedUseSelectorHook } from 'react-redux';
import type { RootState, AppDispatch } from '../store';

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

 

store/counter.ts

import { createSlice } from '@reduxjs/toolkit';
import type { PayloadAction } from '@reduxjs/toolkit';

interface CounterState {
  counter: number;
}

const initialState: CounterState = {
  counter: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    add: (state, action: PayloadAction<number>) => {
      state.counter += action.payload;
    },
    sub: (state, action: PayloadAction<number>) => {
      state.counter -= action.payload;
    },
  },
});

export const counterActions = counterSlice.actions;
export const selectCount = (state: RootState) => state.counter.counter; // 추가
export const default counterSlice.reducer;

 

 

components/Counter.tsx

import React from 'react';
import { useAppSelector, useAppDispatch } from '../hooks';
import { selectCount, counterActions } from '../store/counter';

const Counter = () => {
  const counter = useAppSelector(selectCount);
  const dispatch = useAppDispatch();
  
  const addHandler = () => {
    dispatch(counterActions.add(10));
  };
  
  const subHandler = () => {
    dispatch(counterActions.sub(10));
  };

  return (
    <div>
      <h2>{counter}</h2>
      <div>
        <button onClick={addHandler}>더하기</button>
        <button onClick={subHandler}>빼기</button>
      </div>
    </div>
  );
};

export default Counter;

 

 

4. store 제공하기

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

import { Provider } from 'react-redux';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

 

Provider로 app을 감싸면 된다.

 

참조 : https://redux-toolkit.js.org/tutorials/quick-start

728x90

'👨‍💻 TypeScript' 카테고리의 다른 글

TypeScript - implements  (0) 2024.03.19
TypeScript - declare  (0) 2024.03.19
TypeScript - React + Typescipt  (0) 2024.03.17
TypeScript - TypeScript Generics  (0) 2024.03.04
TypeScript - public, private  (0) 2024.03.04