본문 바로가기
클린코드 샘플(JavaScript)

Redux - reducer(switch문) 리팩토링

by 찬찬2 2022. 11. 15.

■ switch문을 reducer함수에서 분리

- 구조분해 할당으로 매개변수명을 가독성 좋게 만든다.

 

Redux에서 reducer는 보통 아래와 같은 형태를 하고 있다.

 

const store = (() => {
    let state;
    return {
        getState(){
            return state;
        },
        dispatch(action){
            state = reducer(state, action);
        }
    }
})();

const initialState = { todos: [] };

const reducer = (state = initialState, action) => {
    switch(action.type){
        case "addTodo" :
            return {
                ...state,
                todos: [ action.data, ...state.todos]
            };
        case "updateTodo" : {
            const newTodos = [...state.todos];
            newTodos.splice(action.targetIndex, 1, action.data);
            return {
                ...state,
                todos: newTodos
            }
        }
        case "removeTodo" : {
            const newTodos = [...state.todos];
            newTodos.splice(action.targetIndex, 1);
            return {
                ...state,
                todos: newTodos
            }
        }
        default :
            return state;
    }
}

store.dispatch({ type: "addTodo", data: "new1" });
store.dispatch({ type: "addTodo", data: "new2" });

 

일반적으로는 위와 같이 사용한다. 하지만 리팩토링 과정에서 항상 강조하는 "기능별 최소 단위로 쪼개서 관리"를 따르자면... switch문은 규칙성이 보이고 이를 맵핑 데이터로써 reducer 함수에서 빼낼 수 있을 것이다.

 

const reducerMap = {
    "addTodo" : (state, action) => ({
        ...state,
        todos: [ action.data, ...state.todos]
    }),
    "updateTodo" : (state, action) => {
        const newTodos = [...state.todos];
        newTodos.splice(action.targetIndex, 1, action.data);
        return {
            ...state,
            todos: newTodos
        }
    },
    "removeTodo" : (state, action) => {
        const newTodos = [...state.todos];
        newTodos.splice(action.targetIndex, 1);
        return {
            ...state,
            todos: newTodos
        }
        
    }
};

const reducer = (state = initialState, action) => {
    return reducerMap[action.type]?.(state, action) || state;
}

 

reducerMap으로 빼내고 인자로 state와 action을 전달한다. 분리함으로써 관리가 용이해졌지만 여천히 reducerMap이 어수선하다. 3개의 함수가 모두 동일하게 state, action을 받고 있는데 이를 구조분해 할당으로 조금더 리팩토링 해보면...

 

const reducerMap = {
    "addTodo" : (state, {data}) => ({
        ...state,
        todos: [data, ...state.todos]
    }),
    "updateTodo" : (state, { targetIndex, data }) => {
        const newTodos = [...state.todos];
        newTodos.splice(targetIndex, 1, data);
        return {
            ...state,
            todos: newTodos
        }
    },
    "removeTodo" : (state, { targetIndex }) => {
        const newTodos = [...state.todos];
        newTodos.splice(targetIndex, 1);
        return {
            ...state,
            todos: newTodos
        }
        
    }
};

const reducer = (state = initialState, action) => reducerMap[action.type]?.(state, action) || state;

 

1. 구조분해 할당으로 매개변수가 어떤 것인지 명확하게 알 수있게 되었다. 가독성이 올라간 것과 동시에 다른 개발자와 협업 시 코드를 이해하는데 조금 더 편하게 되었다.

 

2. 이렇게 객체와 함수를 외부로 분리하면 공통로직/분기별 실제동작정의가 분리되어 수정사항이 생겨도 공통로직은 건드리지 않아도 되서 편리하다.

댓글