[fe] redux 와 유용한 라이브러리 정리
Redux library
Redux와 함께 쓰면 유용한 라이브러리 정리
1. redux-actions
액션 생성함수를 간단히 작성할 수 있게 도와주고, 리듀서에서 switch문을 생략해주는 라이브러리
코드비교 [기존]
export const increase = () => ({type: INCREASE});
export const decrease = () => ({type: DECREASE});
[사용후]
import {createActions} from 'redux-actions';
export const increase = createActions(INCREASE);
export const decrease = createActions(DECREASE);
[기존]
function counter(state = initialState, action) {
switch(action.type){
case INCREASE:
return { number: state.number+1};
case DECREASE:
return { number: state.number-1};
default:
return state;
}
}
[사용후]
import {handleActions} from 'redux-actions';
const counter = handleActions(
{
[INCREASE]: (state,action) => ({number: state.number +1}),
[DECREASE]: (state,action) => ({number: state.number -1}),
},
initialState
)
action 객체에 파라미터로 넘겨주는 값이 있을때는 payload 라는 객체 안에 데이터가 들어가게 된다.
{type: INCREASE , payload: 'data'}
따라서 handleAction에서 값을 참조하고자 할때 action.payload 안에서 값을 가져와야한다
const todos = handleActions({
[INSERT]: (state,action) => ({...state, todos: state.todos.concat(action.payload)}),
[CHANGE_INPUT]: (state,action) => ({...state, input: action.payload}),
[REMOVE]: (state,action) => ({...state, todos: state.todos.filter((todo) => (todo.id !== action.payload))}),
[TOGGLE]: (state,{payload: id}) => ({...state, todos: state.todos.map((todo) => todo.id === id ? {...todo,done: !todo.done} : todo)})
},initialState)
코드 중간에 보면 모든 값이 payload로만 대체되기 때문에 가독성 부분에서 추후 헷갈릴수가 있다.
따라서 Toggle
케이스에서 보이는것 처럼 {payload: id}
라는 문법을 통해 payload -> id 값으로 대체해주면 가독성 또한 향상될 수 있다.
2. immer
reducer 에서 객체를 복사하고자 할때 객체의 구조가 복잡할수록 전개연산자로 복사할 수 있는 범위가 까다로워 지므로 immer 라이브러리에 이쓴 produce를 사용하여 간단하게 새로운 객체를 생성할 수 있게 사용한다.
import produce from 'immer';
const todos = handleActions({
[INSERT]: (state,{payload:todo}) => produce(state,draft => {draft.todos.push(todo)}),
[CHANGE_INPUT]: (state,{payload:input}) => produce(state,draft => {draft.input = input}),
[REMOVE]: (state,{payload: id}) => produce(state,draft => {
const index = draft.todos.findIndex(todo => todo.id === id);
draft.todos.splice(index,1);
}),
[TOGGLE]: (state,{payload: id}) => produce(state,draft => {
const todo = draft.todos.find(todo => todo.id === id);
todo.done = !todo.done;
}),
}, initialState)
3. Hook과 함께 사용하기
기존 react
라이브러리에서 사용했던 useState, useEffect 등과 같이 함수형 컴포넌트에서 사용하는 hook이 react-redux
라이브러리 안에도 존재한다.
이를 사용하여 좀 더 간결하게 redux 구조를 짜는것이 가능하니 사용하는 방법을 잘 익혀두면 좋을 것 같다.
useSelector, useDispatch
import { useSelector,useDispatch } from 'react-redux'
const CounterContainer = () => {
const number = useSelector(state => state.counter.number);
const dispatch = useDispatch();
return (
<Counter
number={number}
onIncrease= {() => dispatch(increase())}
onDecrease= {() => dispatch(decrease())}
/>
)
}
위의 코드를 자세히 살펴보면 useSelector
훅을 통해 store에 있는 state에 접근하여 사용하고자 하는 number 값을 현재 컴포넌트의 값으로 가져왔다.
마찬가지로 useDispatch()
훅을 통해 store에 이벤트를 호출할 수 있는 dispatch 객체를 가져왔고, 이를 사용하여 store에 있는 action 함수를 호출했다.
기존의 mapStateToProps 와 mapDispatchToProps, connect 이런 redux 구조적인 문법들이 사라지고 조금 더 익숙한 훅 표현 방식으로 더 간략하게 사용되는 장점이 있다.
하지만 이를 더 최적화 처리를 한다면 현재 number 값이 바뀔때마다 onIncrease와 onDecrease 함수가 새로 만들어지고 있기 때문에 useCallback
을 사용하여 함수 생성을 최적화 해줄 수 있다.
<Counter
number={number}
onIncrease= {useCallback(() => dispatch(increase()),[dispatch])}
onDecrease= {useCallback(() => dispatch(decrease()),[dispatch])}
/>
useStore
useStore의 경우 직접 store에 접근하여 state 값을 가져오거나 dispatch 를 호출할때 사용가능하지만 정말 어쩌다가 스토어에 직접 접근하는 경우가 아니라면 보통 쓰진 않는다.