typescript

reduxtoolkit 을 typescript환경에서 사용하기

냠냠맨 2023. 4. 19. 03:21

 

그냥 쓰면 되지 않으려나...했는데 type의 벽에 가로막혀서

이거 어케 쓰냐... 하지말까...란 생각이 들었지만..

아무튼 극복함

이렇게 올리기 버튼을 누르면 숫자가 2 오르고 내리기 버튼을 누르면 숫자가 2 내려가는 버튼을 만들어보겠습니다.

npm install react-redux @reduxjs/toolkit

 

src/modules/CounterSlice.ts
import { createSlice } from '@reduxjs/toolkit';

const CounterSlice = createSlice({
  name: 'CounterSlice',
  initialState: { number: 0 },
  reducers: {
    up: (state, action) => {
      state.number += action.payload;
    },
    down: (state, action) => {
      state.number -= action.payload;
    },
  },
});
export const { up, down } = CounterSlice.actions;
export default CounterSlice.reducer;

대체로 비슷하게 작성합니다.

이렇게 작성해도 에러가 발생하지는 않습니다

하지만 좀 더 타입스크립트스럽게 코드를 바꿔보겠습니다.

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

interface CounterState {
  number: number;
}

const initialState: CounterState = { number: 0 };

const CounterSlice = createSlice({
  name: 'CounterSlice',
  initialState,
  reducers: {
    up: (state, action: PayloadAction<number>) => {
      state.number += action.payload;
    },
    down: (state, action: PayloadAction<number>) => {
      state.number -= action.payload;
    },
  },
});
export const { up, down } = CounterSlice.actions;
export default CounterSlice.reducer;

 

PayloadAction

PayloadAction<number>구문은 up,down 리듀서에 전달된 작업이
number 유형의 payload 속성을 갖도록 지정해줍니다. 

initialState에 interface를 활용해 타입을 지정해주고

action을 PayloadAction타입으로 갖도록 지정해줄 수 있습니다.

PayloadAction에 관련된 코드는

reduxjs/src/createAction.ts 파일에서 확인할 수 있습니다.

export type PayloadAction<
  P = void,
  T extends string = string,
  M = never,
  E = never
> = {
  payload: P
  type: T
} & ([M] extends [never]
  ? {}
  : {
      meta: M
    }) &
  ([E] extends [never]
    ? {}
    : {
        error: E
      })

이런식으로 구현이 되어있는데 저는 타입스크립트를 잘 몰라서

아는 분들이 해석해주시기 바랍니다. 알려줘잉

 

하여간 이렇게 작성하면 모가조음?

타입스크립트가 자동완성을 시켜줍니다.

타입스크립트 자동완성 최고 짱짱


src/modules/index.ts

이번엔 confiugreStore를 이용해 store를 생성해주겠습니다.

이 store를 생성하는 부분은 자동완성을 쓰지 않을것이어도

꼭 따라줘야합니다. 안하면 에러나니까요..

import TodoSlice from './TodoSlice';
import { configureStore } from '@reduxjs/toolkit';
import CounterSlice from './CounterSlice';

const store = configureStore({
  reducer: {
    counter: CounterSlice,
    todoReducer: TodoSlice,
  },
});

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

configureStore를 이용해 작성하는 부분은 똑같이 하면 되는데

type을 export 해줘야합니다.

이 내보낸 타입값은 나중에 아주 유용하게 사용합니다.


src/app.tsx
import './App.css';
import React, { useState } from 'react';
import store from './modules';
import { Provider } from 'react-redux';
import Counter from './components/Counter';

function App() {
  return (
    <Provider store={store}>
      <div>하이요</div>
      <Counter />
    </Provider>
  );
}

export default App;

이 부분도 그냥 똑같이 작성해줍니다.


src/components/Counter.tsx
import React, { ReactNode } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { up, down } from '../modules/CounterSlice';
import { RootState } from '../modules/index'; // import your RootState type from your store file

interface Props {
  number: number;
}

type PropsFromRedux = ConnectedProps<typeof connector>;
type ComponentProps = Props & PropsFromRedux;

const Counter = ({ number, up, down }: ComponentProps) => {
  return (
    <div>
      <div>{number}</div>
      <button
        onClick={() => {
          up(2);
        }}
      >
        올리기
      </button>
      <button
        onClick={() => {
          down(2);
        }}
      >
        내리기
      </button>
    </div>
  );
};

const mapStateToProps = (state: RootState) => ({
  number: state.counter.number,
});
const mapDispatchToProps = { up, down };
const connector = connect(mapStateToProps, mapDispatchToProps);
export default connector(Counter);

연동하는 부분에서 큰 차이가 있습니다.

전 connect 함수를 이용할것인데 그런 경우 다음과 같이 구현하게됩니다.

왜 저런코드가 나오는지는 나중에 공부할게요... 일단은 쓰겠읍니다.

반응형