반응형
리덕스 더 편하게 사용하기
- 액션 생성 함수, 리듀서를 작성할 때 redux-actions라는 라이브러리와 이전에 배웠던 immer 라이브러리를 활용하면 리덕스를 훨씬 편하게 사용할 수 있습니다.
redux-actions
- 액션 생성 함수를 더 짧게 작성할 수 있다.
- switch/case 문이 아닌 handleActions 함수 사용 가능
- 업데이트 함수를 설정하는 형식으로 작성
counter 모듈에 적용하기
// createAction을 이용해 액션 생성 함수 설정
export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);
// handleActions를 이용한 리듀서 함수 설정
const counter = handleActions(
{
[INCREASE]: (state, action) => ({ number: state.number + 1 }),
[DECREASE]: (state, action) => ({ number: state.number - 1 }),
},
initialState,
);
- 첫 번째 인자로 업데이트 함수, 두 번째 함수로 초기 상태 설정
todos 모듈에 적용하기
- 먼저 액션 생성 함수를 교체해 줄 텐데, 조금 다른 점이 있습니다.
- 바로 각 액션 생성 함수에서 파라미터를 필요로 한다는 점입니다.
- createAction으로 액션을 만들면 액션에 필요한 추가 데이터는 payload라는 이름을 사용합니다.
import { createAction } from 'redux-actions';
const CHANGE_INPUT = 'todos/CHANGE_INPUT'; // 인풋 값을 변경함
const INSERT = 'todos/INSERT'; // 새로운 todo를 등록함
const TOGGLE = 'todos/TOGGLE'; // todo를 체크/체크 해제함
const REMOVE = 'todos/REMOVE'; // todo를 제거함
export const changeInput = createAction(CHANGE_INPUT, input => input);
let id = 3; // insert가 호출될 때마다 1씩 더해집니다.
export const insert = createAction(INSERT, text => ({
id: id++,
text,
done: false,
}));
export const toggle = createAction(TOGGLE, id => id);
export const remove = createAction(REMOVE, id => id);
(...)
- insert의 경우 todo 객체를 액션 객체 안에 넣어 주어야 하기 때문에 두 번째 파라미터에 text를 넣으면 todo 객체가 반환되는 함수를 넣어 주었습니다.
- 나머지 함수에는 text => text 혹은 id => id와 같은 형태로 파라미터를 그대로 반환하는 함수를 넣었습니다.
immer
- yarn add immer
- 불변성 유지를 위해 깊어지는 spread 연산자 방지
- Counter 처럼 간단한 경우 immer을 사용하면 더 복잡. Todos모듈에 적용.
- Todos.js
// immer 사용한 reducer
const todos = handleActions(
{
[CHANGE_INPUT]: (state, { payload: input }) =>
produce(state, (draft) => {
draft.input = input;
}),
[INSERT]: (state, { payload: todo }) =>
produce(state, (draft) => {
draft.todos.push(todo);
}),
[TOGGLE]: (state, { payload: id }) =>
produce(state, (draft) => {
const todo = draft.todos.find((todo) => todo.id === id);
todo.done = !todo.done;
}),
[REMOVE]: (state, { payload: id }) =>
produce(state, (draft) => {
const index = draft.todos.findIndex((todo) => todo.id === id);
draft.todos.splice(index, 1);
}),
},
initialState,
);
Hooks를 사용하여 컨테이너 컴포넌트 만들기
useSelector로 상태 조회하기
- useSelector Hook을 사용하면 connect 함수를 사용하지 않고도 리덕스의 상태를 조회할 수 있습니다.
const 결과 = useSelector(상태 선택 함수);
- 여기서 상태 선택 함수는 mapStateToProps와 형태가 똑같습니다.
useDispatch를 사용하여 액션 디스패치하기
- 이 Hook은 컴포넌트 내부에서 스토어의 내장 함수 dispatch를 사용할 수 있게 해 줍니다.
- 컨테이너 컴포넌트에서 액션을 디스패치해야 한다면 이 Hook을 사용하면 됩니다.
const dispatch = useDispatch();
dispatch({ type: ‘SAMPLE_ACTION‘ });
useStore를 사용하여 리덕스 스토어 사용하기
- useStore Hooks를 사용하면 컴포넌트 내부에서 리덕스 스토어 객체를 직접 사용할 수 있습니다.
- 사용법은 다음과 같습니다.
const store = useStore();
store.dispatch({ type: 'SAMPLE_ACTION '});
store.getState();
- useStore는 컴포넌트에서 정말 어쩌다가 스토어에 직접 접근해야 하는 상황에만 사용해야 합니다.
- 이를 사용해야 하는 상황은 흔치 않을 것입니다.
TodosContainer를 Hooks로 전환하기
- 이제 TodosContainer를 connect 함수 대신에 useSelector와 useDispatch Hooks를 사용하는 형태로 전환해 봅시다.
const TodoContainer = () => {
const { input, todos } = useSelector(({ todos }) => ({
input: todos.input,
todos: todos.todos,
}));
const dispatch = useDispatch();
const onChangeInput = useCallback((input) => dispatch(changeInput(input)), [
dispatch,
]);
const onInsert = useCallback((text) => dispatch(insert(text)), [dispatch]);
const onToggle = useCallback((id) => dispatch(toggle(id)), [dispatch]);
const onRemove = useCallback((id) => dispatch(remove(id)), [dispatch]);
return (
<Todos
input={input}
todos={todos}
onChangeInput={onChangeInput}
onInsert={onInsert}
onToggle={onToggle}
onRemove={onRemove}
/>
);
};
export default TodoContainer;
- useSelector를 사용할 때 비구조화 할당 문법을 활용했습니다.
- 또한, useDispatch를 사용할 때 각 액션을 디스패치하는 함수를 만들었는데요.
useActions 유틸 Hook을 만들어서 사용하기
export default function useActions(actions, deps) {
const dispatch = useDispatch();
return useMemo(
() => {
if (Array.isArray(actions)) {
return actions.map(a => bindActionCreators(a, dispatch));
}
return bindActionCreators(actions, dispatch);
},
deps ? [dispatch, ...deps] : deps
);
}
- 방금 작성한 useActions Hook은 액션 생성 함수를 액션을 디스패치하는 함수로 변환해 줍니다.
- 액션 생성 함수를 사용하여 액션 객체를 만들고, 이를 스토어에 디스패치하는 작업을 해 주는 함수를 자동으로 만들어 주는 것이죠.
- useActions는 두 가지 파라미터가 필요합니다. 첫 번째 파라미터는 액션 생성 함수로 이루어진 배열입니다.
- 두 번째 파라미터는 deps 배열이며, 이 배열 안에 들어 있는 원소가 바뀌면 액션을 디스패치하는 함수를 새로 만들게 됩니다.
connect 함수와의 차이점
- connect : props가 바뀌지 않았다면 리렌더링이 자동으로 방지되어 성능이 최적화
- useSelector : 리덕스 상태를 조회했을 때 최적화 작업이 자동으로 이루어지지 않는다.
- React.memo를 컨테이너 컴포넌트에 사용해 준다.
- TodosContainer.js
- export default React.memo(TodosContainer);
반응형
'react를 다루는 기술' 카테고리의 다른 글
18. 리덕스 미들웨어를 통한 비동기 작업 관리(1) (0) | 2022.06.23 |
---|---|
17. 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기(3) (0) | 2022.06.22 |
17. 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기(1) (0) | 2022.06.20 |
16. 리덕스 라이브러리 이해하기 (0) | 2022.06.18 |
15. Context API (0) | 2022.06.17 |