Redux 공식문서 들여다보기_타입스크립트와 사용할 때_3회

기능 사용하기

Slice와 action 타입 정의

각 슬라이스는 최초 state값을 위한 타입이 지정되어야 한다. 그 결과 createSlice는 각 reducer에서 state의 타입을 정확하게 추론할 것이다.

createSlice
redux-toolkit에 포함되어 있는 함수이다. state의 초기 값을 설정할 수 있는 initial state, 슬라이스의 고유 이름을 담당하는 name action 함수가 자동으로 생성되는 reducer 등으로 구성된 객체를 인자로 받는다.
내부적으로, 이 함수는 createActioncreateReducer를 사용한다. 그래서 우리는 불변성을 지닌채 업데이트를 하기 위해 Immer를 사용할 수 도 있다.
기존 redux에 비해 초기 생성 과정이 간략하고, 사용성이 좋아 효율적이다.

생성된 모든 actions는 PayloadAction<T>이라는 Redux Toolkit에서 제공하는 타입을 이용하여 정의한다. 이것은 action.payload의 타입을 받아 각각의 제네릭 인자로 사용된다.

우리는 store파일에 있는 Rootstate를 안전하게 import할 수 있다. 이건 반복되는 import이지만, TS컴파일러는 그 타입을 정확히 지정할 수 있다. 이건 selector 함수를 사용할 때 필요할 것이다. 원하는 state를 store로 부터 지정하는 selector함수는 Rootstate라는 타입을 지정해야 store에서 state를 꺼내어 반환되는 결과를 얻을 수 있다.

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>

생성된 action 생성자들은 PayloadAction<T>을 기반이된 payload 인자를 받아 올바르게 타입이 지정 될 것이다. 예를 들어, 아래와 같은 incrementByAmountaction 생성자가 있다고 해보자. 이것은 제네릭으로 number 타입의 인자를 받는 것으로 타입 설정을 마쳤으므로, number 타입만이 payload로 들어와야할 것이다.

 // Use the PayloadAction type to declare the contents of `action.payload`
    incrementByAmount: (state, action: PayloadAction<number>) => {

어떤 경우에는, 타입스크립트가 initial state에 대한 타입 지정이 불필요할 정도로 엄격할 수 있다. 만약 그런 경우가 발생할 때, 우리는 변수의 타입을 정의하는 것 대신에 as문법을 이용하여 initial state를 캐스팅하는 것으로 대처할 수 있다.

// Workaround: cast state instead of declaring variable type
const initialState = {
  value: 0
} as CounterState

컴포넌트에서 타입이 지정된 Hooks 사용하기

우리가 사전에 React-Redux의 hooks에 타입을 지정해준다면 이후 컴포넌트에서 import하여 사용하며, 반복되는 타입 지정없이 사용할 수 있을 것이다.

import React, { useState } from 'react'

import { useAppSelector, useAppDispatch } from 'app/hooks'

import { decrement, increment } from './counterSlice'

export function Counter() {
  // The `state` arg is correctly typed as `RootState` already
  const count = useAppSelector(state => state.counter.value)
  const dispatch = useAppDispatch()

  // omit rendering logic
}

위 코드를 보면, 사전에 타입을 지정했던 useAppSelectoruseAppDispatch는 각각 < Rootstate >, < AppDispatch > 타입을 통해 별도의 타입 지정 없이 사용할 수 있는 점에서 편리하고, 효율적이다.

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

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

정리

Redux Toolkit을 타입스크립트와 사용할 경우, createSlice와 hooks등 적절할 타입을 지정하는 것은 사용성을 높인다.
action 생성자: PayloadAction 타입 지정
useSelector: store.getState로부터 온 Rootstate 타입 지정
useDispatch: store.dispatch로부터 온 AppDispatch 타입 지정


참고 자료: Redux 공식 문서

좋은 웹페이지 즐겨찾기