블로그만들기(6) - typesafe-actions

anjoy·2021년 2월 18일
0

블로그만들기

목록 보기
6/13
post-thumbnail

이전의 블로그와 다르게 이번에는 TypeScript로 애플리케이션을 구현하고 있는데 redux, redux-saga를 활용하려고 하는 부분에서 불편한 점이 많았고, 참고한 여러 포스트에서 typesafe-actions라는 모듈을 활용하여 Reducer코드를 리팩토링하는 포스트와 코드를 꽤 보았기에 저도 학습하여 사용하고자 했습니다.

Typesafe-actions

참고 - goonerholic님의 typesafe-actions 활용하기

typesafe-actions는 Redux 아키텍쳐에서 Reduce의 다변성과 복잡성을 감소시키기위해 설계된 Typesafe 유틸리티입니다.

createAction

단순 액션 생성함수입니다.

import { createAction } from 'typesafe-actions';

const ACTION = 'ACTION';

const action = createAction(ACTION, ({ ...parameters }) => ({ ...parameters }))()

typesafe-actions를 사용하는데 createAction을 호출해주지 않으면 에러가 발생합니다.

createAsyncAction

비동기 액션을 생성시켜주는 함수입니다.

import { createAsyncAction } from 'typesafe-actions';

interface requestPayloadType {
  ...
};
  
interface successPayloadType {
  ...
}; 

const ACTION_REQUEST = 'ACTION_REQUEST';
const ACTION_SUCCESS = 'ACTION_SUCCESS';
const ACTION_FAILURE = 'ACTION_FAILURE';

const postingAsync = createAsyncAction(
  ACTION_REQUEST,
  ACTION_SUCCESS,
  ACTION_FAILURE
)<requestPayloadType, successPayloadType, Error>();

만약 Axios를 활용하는 경우 requestPayloadType, AxiosResponse<successPayloadType>, AxiosError와 같은 형식을 generic 인수로 입력해주면 됩니다.

createAction과 마찬가지로 함수를 호출해주어야지 액션 생성함수가 정상적으로 생성됩니다.

createReducer

리듀서를 생성해주는 함수입니다.

import { createReducer } from 'typesafe-actions';

// createAction action

const reducer = createReducer(initialState, {
  [ACTION, ACTION2]: (state, action) => ({
    ...state,
    key: action.payload.value
  })
});

위와 같이 리듀서를 생성할 수 있고 배열을 통한 복수 케이스의 처리도 가능합니다.

그 외

createCustomAction, AsyncActionCreator 등의 메서드가 있지만 이후에 코드 리팩토링을 진행하면서 자세하게 살펴볼 예정입니다.

적용

npm i --save typesafe-actions

먼저 typesafe-actions 모듈을 install 해주었습니다.

import { createReducer, createAsyncAction, ActionType } from 'typesafe-actions';
import { ICategoryHead, IPost, IPostsState } from '@typings/datas';
import { AxiosError, AxiosResponse } from 'axios';

const initialState: IPostsState = {
	Category: [],
	posts: [],
	numberOfPosts: 0,
	isLoaddingPosts: false,
	isLoadedPosts: false,
	loadPostsErrorReason: null,
	EndOfPosts: false,
};

export const LOAD_POSTS_REQUEST = 'posts/LOAD_POSTS_REQUEST';
export const LOAD_POSTS_SUCCESS = 'posts/LOAD_POSTS_SUCCESS';
export const LOAD_POSTS_FAILURE = 'posts/LOAD_POSTS_FAILURE';

export const loadPostsAsync = createAsyncAction(LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE)<
	null,
	AxiosResponse<IPost[]>,
	AxiosError
>();

const actions = {
	loadPostsAsync
};

type PostsAction = ActionType<typeof actions>;

const postsReducer = createReducer<IPostsState, PostsAction>(initialState, {
	[LOAD_POSTS_REQUEST]: (state) => ({
		...state,
		isLoaddingPosts: true,
	}),
	[LOAD_POSTS_SUCCESS]: (state, { payload }) => ({
		...state,
		isLoaddingPosts: false,
		posts: state.posts.concat(payload.data),
		EndOfPosts: payload.data.length !== 8,
	}),
	[LOAD_POSTS_FAILURE]: (state, { payload: error }) => ({
		...state,
		isLoaddingPosts: false,
		loadPostsErrorReason: 'error',
	})
});

export default postsReducer;

위와 같이 테스트를 위한 간단한 리듀서를 작성하였습니다.

다음 포스트에서는 Saga를 연결한 뒤 post를 정상적으로 불러오는 지 테스트해보겠습니다.

profile
안녕하세요 벨로그에서 자기소개를 한 줄로 쓰라고 되어있는데 최대한 한 줄로 자기 소개를 해보겠습니다. 제 이름은 .....

0개의 댓글