April 21, 2020
지난번 타입스크립트 + 리덕스로 투두리스트를 만든것을 typesafe-actions라는 라이브러리를 사용하여 더 편한 방법으로 코드를 개선하는 과정.
npm install typesafe-actions
라이브러리 설치
src/modules/counter.ts
import { createAction, ActionType, createReducer } from 'typesafe-actions'
3가지 유틸 함수 및 타입을 불러온다. 전버전에서는 createStandardAction이었으나 createAction으로 바뀌었다.
const INCREASE = 'counter/INCREASE'
const DECREASE = 'counter/DECREASE'
const INCREASE_BY = 'counter/INCREASE_BY'
액션생성함수를 만들 때 createAction을 활용한다.
export const increase = createAction(INCREASE)()
export const decrease = createAction(DECREASE)()
export const increaseBy = createAction(INCREASE_BY)<number>()
액션의 페이로드로 들어가는 값은 제너릭을 사용해 정해주고 페이로드에 아무것도 필요없다면 제너릭을 생략한다.
가끔 액션생성함수로 파라미터로 넣어주는 값과 페이로드값이 일치하지 않는다면?
const createItem = (name: string) => ({
type: CREATE_ITEM,
payload: { id: nanoid(), name },
})
이렇게 작성해준다
const createItem = createStandardAction(CREATE_ITEM).map(name => ({
payload: { id: nanoid(), name },
}))
이전에는 ReturnType을 사용하여 선언했다.
const actions = { increase, decrease, increaseBy }
type CounterAction = ActionType<typeof actions>
ActionType을 사용해 actions라는 객체에 모든 액션생성함수를 넣고 액션타입으로 감싸준다. 간편해졌다.
createReducer를 활용해 스위치케이스문이 아닌 object map형태로 구현할 수 있어서 코드가 간결해진다.
const counter = createReducer<CounterState, CounterAction>(initialState, {
[INCREASE]: state => ({ count: state.count + 1 }),
[DECREASE]: state => ({ count: state.count - 1 }),
[INCREASE_BY]: (state, action) => ({ count: state.count + action.payload }),
})
createReducer에서는 제너릭으로 상태의 타입과 액션들의 타입을 넣어준다. 이를 사용해 내부에 각각 액션들을 위해 구현할 함수에서 타입을 추론한다.
object map 방식ㅇ니 아닌 메소드 체이닝 방식으로 구현도 가능하다.
const counter = createReducer<CounterState, CounterAction>(initialState)
.handleAction(INCREASE, state => ({ count: state.count + 1 }))
.handleAction(DECREASE, state => ({ count: state.count - 1 }))
.handleAction(INCREASE_BY, (state, action) => ({
count: state.count + action.payload,
}))
handleAction 함수에 첫번째 매개변수에는 액션의 타입을 넣어주고 두번째에는 업데이터 함수를 넣어준다.
방식은 counter를 했던것처럼 동일하다.
src/modules/todos.ts
import { createAction, ActionType, createReducer } from 'typesafe-actions'
const ADD_TODO = 'todos/ADD_TODO'
const TOGGLE_TODO = 'todos/TOGGLE_TODO'
const REMOVE_TODO = 'todos/REMOVE_TODO'
export const addTodo = createAction(ADD_TODO)<string>()
export const toggleTodo = createAction(TOGGLE_TODO)<number>()
export const removeTodo = createAction(REMOVE_TODO)<number>()
// 액션들의 타입스크립트 타입 준비
const actions = { addTodo, toggleTodo, removeTodo }
type TodosAction = ActionType<typeof actions>
const todos = createReducer<TodosState, TodosAction>(initialState, {
[ADD_TODO]: (state, { payload: text }) =>
state.concat({
id: Math.max(...state.map(todo => todo.id)) + 1,
text,
done: false
}),
[TOGGLE_TODO]: (state, { payload: id }) =>
state.map(todo => (todo.id === id ? { ...todo, done: !todo.done } : todo)),
[REMOVE_TODO]: (state, { payload: id }) =>
state.filter(todo => todo.id !== id)