웹/React
Redux 적용하기 with Typesciprt, Redux toolkit
개발하고 기록하는 개발자
2022. 4. 21. 01:15
1. Redux Toolkit 과 React-Redux 설치
npm install @reduxjs/toolkit react-redux
2. Redux Store 생성하기
// app/store.js
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {},
})
// store 에 있는 State 타입과 Dispatch 타입 지정
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
3. React index 파일에 Provider 추가
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
4. Redux State Slice 파일 만들기
//features/counter/counterSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import type { RootState } from '../../app/store'
// Define a type for the slice state
interface CounterState {
value: number
}
// Define the initial state using that type
const initialState: CounterState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
// `createSlice` will infer the state type from the `initialState` argument
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
// Other code such as selectors can use the imported `RootState` type
export const selectCount = (state: RootState) => state.counter.value
export default counterSlice.reducer
- createSlice : slice 의 이름을 식별하기 위한 이름, 초기 값(initial state value), 하나 또는 여러개의 reducer 함수가 parameter 로 필요하다.
- Redux Toolkit 의 createSlice 와 createReducer APIs 는 Immer 를 바로 사용할 수 있다.
5. Slice Reducers 를 store 에 추가하기
//app/store.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
6. React Components 에서 Redux State 와 Actions 사용하기
import React from 'react'
import { RootState } from '../../app/store'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
export function Counter() {
const count = useSelector((state: RootState) => state.counter.value)
const dispatch = useDispatch()
return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
)
}
7. Typed Hooks 정의하기
//app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
단순히 RootState 와 AppDispatch 를 각 Components 에서 import 해서 사용할 수도 있다. 하지만 Redux Toolkit 개발자들은 typed 된 useDispatch 와 useSelector hook 을 만들어 사용하는 것을 추천하고 있다 이유는 다음과 같다.
1. useAppSelector 를 정의하면 (state:RootState) 라고 매번 부를 필요가 없다
2. Default Dispatch 는 thunks 를 모른다. 그래서 thunks 를 올바르게 dispatch 하기 위해서는 thunk 미들웨어 타입이 포함된 store 에서 커스텀된 AppDispatch를 사용해야한다.
이때 useAppDispatch 와 같이 미리 typed 해 놓으면 AppDispatch 를 import 하는 것을 보장해준다.
※Thunk : 컴퓨터 프로그래밍에서, 썽크(Thunk)는 기존의 서브루틴에 추가적인 연산을 삽입할 때 사용되는 서브루틴이다.
※주의사항 : useAppDispatch 나 useAppSelector 처럼 pre-typed 하는 경우 꼭 store 설정 파일과 다른 파일에다가 정의해야한다
만약 같은 파일에 둔다면 import dependency issues 가 발생할 수도 있다.
8. Typed Hook 사용한 버전
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
}
참고 자료 : https://redux-toolkit.js.org/tutorials/typescript, https://redux-toolkit.js.org/tutorials/quick-start