리덕스를 사용할때 기본적으로 만들어야 하는 코드는 다음과 같다.
이 4가지를 모두 수행해야 한다.
그렇다면 상태와 action이 굉장히 많은 경우에는 어떻게 될까?
일단 리덕스를 편하게 사용하기 위한 액션 타입, 액션 생성함수들의 코드량이 엄청 많을뿐만 아니라 액션들의 이름도 겹칠 수 있다. 또한 리덕스의 상태는 읽기 전용이다. 값을 하나만 바꾸고자 한다고
해도 기존 상태값을 복사해와서 계속해서 사용해야 한다. 리듀서 함수도 점점 길어지고 거대해질 것이다.
이러한 문제점을 해결하고자, 리덕스에서는 리덕스를 조금더 간편하게 사용할 수 있는 toolkit
을 만들었다.
// 이전 코드
const counterReducer = (state = initialState, action) => {
if (action.type === INCREASE) {
return {
counter: state.counter + 1,
};
}
if (action.type === DECREASE) {
return {
counter: state.counter - 1,
};
}
return state;
};
export default counterReducer;
// 이랫던 reducer 부분을
const counterReducer = (state = initialState, action) => {
if (action.type === INCREASE) {
return {
state.counter + 1,
};
}
if (action.type === DECREASE) {
return {
state.counter - 1,
};
}
return state;
};
export default counterReducer;
// 이런식으로 수정할 수 있다.
redux toolkit
에서 상태의 불변성을 지켜주므로, 상태값에 그대로 접근해서 상태를 수정할 수 있다. 사실 위에 코드는 연습을 위해 작성한 코드로 그게 뭐가 달라진건지 모를 수 도있는데, 기존에 리덕스의 상태를 수정하려면 보통 기존의 상태를 복사해서 사용해야 한다는 점을 주의깊게 살펴본다면 이거 하나만으로 크게 바꼈구나를 알 수 있다.
덧붙여, 액션생성자 함수나 액션타입을 정의해주는 것을 하지 않아도 된다. toolkit
에서 자체적으로 액션생성자함수를 만들어 주므로 우리는 간단하게 사용할 수 있다.
여기서 부터는 위의 예제코드와 달리 유데미의 react
강의를 들으며 기록했던 코드이다.
const initialState = {
counter: 0,
showCounter: true,
};
createSlice({
name: 'counter',
// 모든 slice는 이름이 필수
initialState,
// 위의 초기 상태를 그대로 가져올 수 있음
reducers: {
// 이 reducers 의 이름은 바꿀 수 없다. 오타나니까 actions 객체가 안만들어져서 깜짝 놀람
increment(state) {
state.counter++;
// 예전에는 이렇게 하면안되었지만,
// 여기서는 이렇게 보일뿐 기존상태를 변경하지 않고 새로운 상태를 반환한다.
// 어차피 redux toolkit에서는 기존 상태를 변경불가로-이뮤터블로 둔다.
// 조금 더 쉽게 리덕스를 사용할 수 있다.
// 기존에는, ...state, state.counter + 1
// 로 사용했을 것이다.
},
decrement(state) {
state.counter--;
},
// 위 두개는 payload가 없으므로 action도 필요없다.
increase(state, action) {
state.counter += action.amount;
},
// payload가 필요한 부분은 action이 필요하다.
toggle(state) {
state.showCounter = !state.showCounter;
},
},
// 모든 슬라이스는 리듀서를 필요로 함.
// 여기에다가 메서드를 추가하면 됨.
// 나중에 이 이름이 중요한 역할을 함
// action은 필요 없음 <- 어떤 action을 했음에 따라 함수를 호출할것임
});
// 객체를 인자로 받는다.
// 위에 만들어둔 slice를 이와 같이 store에 연결할 수 있다.
const store = createStore(counterSlice.recuders);
// 이렇게 스토어를 만들어서 연결하면 된다. 스토어가 여러개라면 아래와 같이
const store = configureStore({
reducer: counterSlice.reducer
});
// configureStore를 활용한다.
// index.js (store)
export const counterActions = counterSlice.actions;
// counterActions를 export 한다.
// 그리고 이 액션이 필요한 파일로 간다.
// 위에 만들었던 액션생성자함수, 액션객체를 알아서 만들어준다.
// 우리는 단순히 이런식으로 액션들을 넘겨주면 된다.
// counterActions를 사용하는 곳에서,
// Counter.js
import { useSelector, useDispatch } from 'react-redux';
import { counterActions } from '../store/index';
import classes from './Counter.module.css';
const Counter = () => {
const dispatch = useDispatch();
const counter = useSelector((state) => state.counter);
const show = useSelector((state) => state.showCounter);
const toggleCounterHandler = () => {
dispatch(counterActions.toggle());
// 기존에 액션생성자함수를 사용해서 dispatch하던것과 비슷하게 그대로 사용할 수 있다.
};
const incrementHandler = () => {
dispatch(counterActions.increment());
};
const decrementHandler = () => {
dispatch(counterActions.decrement());
};
const increaseHandler = () => {
dispatch(counterActions.increase(5));
// { type : SOME_UNIQUE, payload : value }
};
// 이런식으로 들어가기 때문에 새로받는 요소는 payload로 작성해주어야 한다.
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{show && <div className={classes.value}>{counter}</div>}
<div>
<button onClick={incrementHandler}>INCREMENT</button>
<button onClick={increaseHandler}>INCREMENT by 5</button>
<button onClick={decrementHandler}>DECREMENT</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;