WEB/REACT

[REACT] redux에 typescript적용하기

자바칩 프라푸치노 2021. 12. 17. 11:00

리덕스에서 타입스크립트를 사용하는 법을 알아보자

출처 : 벨로퍼트

[요약]

1. 모듈에서 액션에 as const붙이기

2. state와 action의 타입 정해주기

3. 리듀서의 반환값과 state의 반환값 일치 시키기

4. Root리듀서에서 RootState export하기

5. index.tsx에 provider에 루트리듀서로 만든 store주입시키기

6. 컴포넌트에서 props 타입 정하기

7. 컴포넌트에서 useSelector로 가져올때 RootState로 지정해서 가져오기

 

 

 

 


1. modules/counter.ts

★ action에 as const를 붙여준다.

나중에 액션 객체를 만들 때 action.type을 추론하는 과정에서 string으로 추론되지 않고

'counter/INCREASE'와 같이 추론된다. 

 

★ state와 action을 type으로 선언해주고 리듀서에서는 state와 함수의 반환값이 일치하도록 한다.

여기에서는 state가 CounterState니까 함수의 반환값도 CounterState이다.

const INCREASE= 'counter/INCREASE' as const;
const DECREASE = 'counter/DECREASE' as const;
const INCREASE_BY = 'couter/INGREASE_BY' as const;

export const increase = () => ({type: INCREASE});
export const decrease = () => ({type : DECREASE});
export const increaseBy = (diff : number) => ({
    type : INCREASE_BY,
    payload : diff
});

type CounterState = {
    count : number;
}
const initialState: CounterState = {
    count : 0
}

type CounterAction = 
    | ReturnType<typeof increase>
    | ReturnType<typeof decrease>
    | ReturnType<typeof increaseBy>


function counter(state: CounterState = initialState, action : CounterAction) : CounterState{
    switch (action.type){
        case INCREASE:
            return { count : state.count + 1}
        case DECREASE:
            return { count : state.count  - 1}
        case INCREASE_BY: 
            return { count : state.count + action.payload }
        default : 
            return state
    }
}

export default counter;

 

 

 

2. modules/index.ts

새로운 모듈이 생길 수도 있으니까 루트 리듀서를 하나 만든다. 

RootState를 내보내는 이유는 이 타입을 컴포넌트에서 불러와서 사용할 일이 있기 때문이다.

import { combineReducers } from "redux";
import counter from "./counter";

const rootReducer = combineReducers({
    counter
})
export default rootReducer;
export type RootState = ReturnType<typeof rootReducer>

 

 

 

3. index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './modules';

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

 

 

 

 

4. src/components/Counter.tsx

Props의 타입을 정해주고 컴포넌트에서 props를 받아올때 타입을 지정해준다. 

import React from 'react';

type CounterProps {
  count: number;
  onIncrease: () => void;
  onDecrease: () => void;
  onIncreaseBy: (diff: number) => void;
}

function Counter({
  count,
  onIncrease,
  onDecrease,
  onIncreaseBy
}: CounterProps) {
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
      <button onClick={() => onIncreaseBy(5)}>+5</button>
    </div>
  );
}

export default Counter;

 

 

 

5. src/components/CounterContainer.tsx

★ useSelector로 불러올때 state를 아까 export했던 RootState로 지정해준다. 

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../modules';
import { increase, decrease, increaseBy } from '../modules/counter';
import Counter from '../components/Counter';

function CounterContainer () => {
  // 상태를 조회합니다. 상태를 조회 할 때에는 state 의 타입을 RootState 로 지정해야합니다.
  const count = useSelector((state: RootState) => state.counter.count);
  const dispatch = useDispatch(); // 디스패치 함수를 가져옵니다

  // 각 액션들을 디스패치하는 함수들을 만들어줍니다
  const onIncrease = () => {
    dispatch(increase());
  };

  const onDecrease = () => {
    dispatch(decrease());
  };

  const onIncreaseBy = (diff: number) => {
    dispatch(increaseBy(diff));
  };

  return (
    <Counter
      count={count}
      onIncrease={onIncrease}
      onDecrease={onDecrease}
      onIncreaseBy={onIncreaseBy}
    />
  );
};

export default CounterContainer;

 

 

 

6. App.tsx에서 불러오기

 

 

728x90