$yarn add redux-actions
redux-actions: 액션 생성 함수를 더 편리하게 .
createAction 함수: 매번 객체 직접 생성할 필요 없이 간단하게 액션 생성 함수 선언 가능
modules/counter.js
import { createAction, handleActions } from "redux-actions";
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE'
export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);
const initialState = {number: 0};
const counter = handleActions(
{
[INCREASE] : (state, action) => ({number: state.number+1}),
[DECREASE] : (state, action) => ({number: state.number-1}),
},
initialState,
);
export default counter;
handleActions로 리듀서 재작성
createAction으로 만든 액션 생성 함수 => action.payload라는 이름 넣어주기
-> 기존 업데이트 로직에서도 모두 action.payload 값을 조회하여 업데이트하도록 구현 필요
액션 생성 함수: 액션에 필요한 추가 데이터 모두 payload라는 이름으로 사용
action.id , action.todo 조회하는 대신 모두 action.payload 값을 조회하도록 리듀서 구현
const todos = handleActions(
{
[CHANGE_INPUT]: (state, action) => ({...state, input: action.payload}),
[INSERT]: (state, action) => ({
...state,
todos: state.todos.concat(action.payload),
}),
[TOGGLE]: (state, action) => ({
...state,
todos: state.todos.map(todo =>
todos.id === action.payload ? {...todo, done: !todo.done} : todo),
}),
[REMOVE]: (state, action) => ({
...state,
todos: state.todos.filter(todos => todos.id !== action.payload),
}),
},
initialState,
);
객체 비구조화 할당 문법 사용
const todos = handleActions(
{
[CHANGE_INPUT]: (state, {payload: input}) => ({
...state, input: input}),
[INSERT]: (state, {payload: todo}) => ({
...state,
todos: state.todos.concat(action.payload),
}),
[TOGGLE]: (state, {payload: id}) => ({
...state,
todos: state.todos.map(todo =>
todos.id === id ? {...todo, done: !todo.done} : todo),
}),
[REMOVE]: (state, {payload: id}) => ({
...state,
todos: state.todos.filter(todos => todos.id !== id),
}),
},
initialState,
);
immer
$yarn add immer
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,
useSelector
connect 사용하지 않고도 리덕스 상태 조회 가능
const 결과 = useSelector(상태 선택 함수);
CounterContainer.js
import { useSelector } from "react-redux";
const CounterContainer = () => {
const number = useSelector(state => state.counter.number);
return <Counter number={number} />;
};
export default CounterContainer;
counter.number 값 조회 => Counter에게 props 넘겨줌
useDispatch
const dispatch = useDispatch();
dispatch({type: 'SAMPLE_ACTION'});
const CounterContainer = () => {
const number = useSelector(state => state.counter.number);
const dispatch = useDispatch();
return <Counter
number={number}
onIncrease={() => dispatch(increase())}
onDecrease={() => dispatch(decrease())}
/>;
};
useCallback으로 액션 디스패치 함수 감싸주기
why? 컴포넌트 성능 최적화
const CounterContainer = () => {
const number = useSelector(state => state.counter.number);
const dispatch = useDispatch();
//컴포넌트 리렌더링 될 때마다 증가, 감소함수 각각 새롭게 만들어짐.
//useCallback을 사용하여 컴포넌트 최적화 필요
const onIncrease = useCallback(() => dispatch(increase()), [dispatch()]);
const onDecrease = useCallback(() => dispatch(decrease()), [dispatch()]);
return <Counter
number={number}
onIncrease={onIncrease()}
onDecrease={onDecrease()}
/>;
};
containers/TodoContainer.js
import {bindActionCreators} from "redux";
import {useSelector, useDispatch} from "react-redux";
import {connect} from 'react-redux';
import Todos from '../components/Todos';
import {changeInput, insert, toggle, remove} from "../modules/todos";
import {useCallback} from "react";
const TodosContainer = () => {
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 TodosContainer;
connect 함수와 hook 사용 차이점
connect 함수: 자동으로 리렌더링 방지, 성능 최적화
useSelector 사용시 -> React.memo 사용하여 추가적으로 최적화 작업 필요
'FE > React & TS' 카테고리의 다른 글
[Socket.io] socket.emit과 socket.on (0) | 2024.03.29 |
---|---|
[Vite+React] 깃허브 페이지 배포 이미지 경로 에러 (0) | 2024.03.16 |
Redux를 활용한 투두리스트 구현 (0) | 2023.10.10 |
Todo app (0) | 2023.07.21 |
Sass / styled-components (0) | 2023.05.16 |