Redux-Toolkit
은 Redux의 몇 가지 특징을 단순화한 것이다.
store
파일에 createSlice
또는 createReducer
를 import해줘야 합니다.
createSlice: 초기 상태, 리듀서 함수로 가득 찬 객체 및 "슬라이스 이름"을 받아들이고 리듀서 및 상태에 해당하는 액션 생성자와 액션 유형을 자동으로 생성하는 함수입니다.
✅학습한 강의에서는 createReducer보다 createSlice의 기능이 더 강력하다. 라고 나옴
createReducer: Redux 감속기 기능 생성을 간소화하는 유틸리티입니다.
내부적으로 Immer를 사용하여 리듀서에 "변형" 코드를 작성하여 불변 업데이트 로직을 크게 단순화하고 특정 액션 유형을 케이스 리듀서 함수에 직접 매핑하여 해당 액션이 발송될 때 상태를 업데이트하도록 지원합니다.
✅ store.js
// configureStore를 이용하여 여러 reducer를 하나의 reducer로 쉽게 합칠 수 있습니다.
// createSlice는 단일 reducer만 처리합니다.
import { createSlice, configureStore } from "@reduxjs/toolkit";
const initialState = {
counter: 0,
showCounter: true,
};
// counterSlice는 전역상태의 slice가 됩니다. 이 slice는 counter와 작동해야 합니다.
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment(state) {
state.counter++; // createSlice 메서드를 이용하면 redux의 기존 state를 바꿀 수 없습니다. 기존의 redux와 똑같이 최신 snapshot으로 덮어씌울뿐입니다.
}, // 해당 메서드들은 나중에 리덕스에 의해 호출되고 현재 상태를 받습니다. 여기서는 action이 필요없습니다. 어떤 action을 했느냐에 따라 여기있는 메서드들이 호출되기 때문입니다.
decrement(state) {
state.counter--;
}, // (위에 이어서) 대신 나중에 서로 다른 리듀서를 구별해놓고, 각각의 리듀서에 해당하는 action을 발생시킵니다.
increase(state, action) {
// payload 지정 (action을 지칭함)
state.counter = state.counter + action.value; // state를 변경하는 것 처럼 보이지만, state를 변경하고 있지 않습니다. 새로운 값으로 덮어씌우는 겁니다.
},
toggleCounter(state) {
state.toggleCounter = !state.toggleCounter;
},
}, // 리듀서는 객체 혹은 map이라 할 수 있다. 여기있는 slice는 리듀서를 필요로 한다. reducer의 값으로 들어가는 객체 안에 메서드를 추가하면 됩니다. 여기가 중요한 역할을 합니다.
}); // 객체를 인자로서 생성합니다. 해당 메서드로 전역 상태의 slice를 미리 만들어놔야 합니다. 여기서는 counter이름을 가진 slice를 생성합니다. createSlice 메서드를 이용하면 redux의 기존 state를 바꿀 수 없습니다. 최신 snapshot으로 덮어씌울뿐입니다.
// reducer : key : {reducer.name : 선언한_slice.reducer ... } 이런 방식으로 여러 슬라이스의 리듀서를 하나로 합칠 수 있습니다.
// store를 생성할 때, createStore(counterSlice.reducer)로 기입하면 slice에서 설정한 리듀서 하나에 접근할 수 있음.
// configureStore는 여러 리듀서를 묶어서 하나로 합칠 수 있음. 이 때, 넘겨주는 값은 리듀서 함수가 아니라 객체를 넘겨줘야 한다.
// createStore는 하나의 리듀서를 처리함.
const store = configureStore({
reducer: counterSlice.reducer,
});
// reducer영역에 있는 메서드에 접근하기 위해서는 `counterSlice.action.REDUCER_METHOD`식으로 접근하면 됩니다.
// 아래와 같이 메서드를 불러오면, '액션 생성자'로 불립니다. 액션 객체를 생성해주기 때문입니다.
// 이런 객체는 이미 액션마다 다른 고유 식별자와 함께 type 속성을 가지고 있습니다. 자동으로 처리되기 때문에 액션 식별자에 대해 신경 쓸 필요가 없다.
// 이렇게 하고 액션이 필요한 컴포넌트로 넘어가면 됩니다.
export const counterActions = counterSlice.actions;
export default store;
✅ Counter.jsx
import { useSelector, useDispatch } from "react-redux";
import { counterActions } from "../store/index";
// 리듀서 메서드 이름들을 key로 가진 객체입니다.
import classes from "./Counter.module.css";
const Counter = () => {
const dispatch = useDispatch();
// dispatch가 store에서 state와 action을 가지고 있는 객체임.
const counter = useSelector((state) => state.counter);
const show = useSelector((state) => state.showCounter);
// 리덕스가 관리하는 state를 받고, 우리가 추출하려는 state 부분을 return한다.
// useSelector는 store에 있는 기본 state 상태 ~ 최신화 된 상태를 바로 사용할 수 있게 해준다.
const incrementCounter = () => {
dispatch(counterActions.increment());
};
const decrementCounter = () => {
dispatch(counterActions.decrement());
};
const increaseCounter = () => {
// action payload를 보내줄 때는 생성자 함수 안에 값을 넣어서 전달해주면 된다.
// 이 때, store에서 받는 값을 꼭 payload라고 명시해줘야한다.
// payload 이름으로 추가 데이터가 전달되기 때문임.
dispatch(counterActions.increase(5));
};
const toggleCounterHandler = () => {
dispatch(counterActions.toggleCounter());
};
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
{show && <div className={classes.value}>{counter}</div>}
<div className={classes.button}>
<button onClick={incrementCounter}>+</button>
<button onClick={increaseCounter}>+5</button>
<button onClick={decrementCounter}>-</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;
✅ 아래 설명은 store.js
코드를 기준으로 작성합니다.
1️⃣ import { createSlice, configureStore } from "@reduxjs/toolkit"
부분입니다.
configureStore
를 이용하여 여러 reducer를 하나의 reducer로 쉽게 합칠 수 있습니다.
2️⃣ const counterSlice = createSlice({})
부분입니다.
- counterSlice는 전역상태의 slice가 됩니다. 이 slice는 counter와 작동해야 합니다.
- 객체를 인자로서 생성합니다. 해당 메서드로 전역 상태의 slice를 미리 만들어놔야 합니다. 여기서는 counter이름을 가진 slice를 생성합니다. createSlice 메서드를 이용하면 redux의 기존 state를 바꿀 수 없습니다. 최신 snapshot으로 덮어씌울뿐입니다.
createSlice
메서드를 이용하면 redux의 기존 state를 바꿀 수 없습니다. 기존의 redux와 똑같이 최신 snapshot으로 덮어씌울뿐입니다.
3️⃣ reducers : {imcrement(state) {...} ...}
부분입니다.
- 해당 메서드들은 나중에 리덕스에 의해 호출되고 현재 상태를 받습니다. 동적인 값을 전달받지 않는다면 action을 지정하지 않아도 됩니다.
- 동적인 값을 전달해야한다면 action을 추가해야합니다. 어떤 action을 했느냐에 따라 여기있는 메서드들이 호출되기 때문입니다.
위 코드의 increse(state, action)이 예시입니다.
4️⃣ const store = configureStore({ reducer: counterSlice.reducer, });
부분입니다.
configureStore
는 여러 리듀서를 묶어서 하나로 합칠 수 있음. 이 때, 넘겨주는 값은 리듀서 함수가 아니라 객체를 넘겨줘야 한다.
createStore
는 하나의 리듀서를 처리함.reducer : key : {reducer.name : 선언한_slice.reducer ... }
이런 방식으로 여러 슬라이스의 리듀서를 하나로 합칠 수 있습니다.- store를 생성할 때,
createStore(counterSlice.reducer)
로 기입하면 slice에서 설정한 리듀서 하나에 접근할 수 있음.
createStore
➡️ 단일 리듀서 사용시
configureStore
➡️ 여러 리듀서를 사용할 수 있게 하나로 합쳐줌- reducer영역에 있는 메서드에 접근하기 위해서는
counterSlice.action.REDUCER_METHOD
식으로 접근하면 됩니다.아래와 같이 메서드를 불러오면,
액션 생성자
로 불립니다.
counterSlice.action.increment
(메서드 이름만 불러옵니다.)
액션 객체를 생성해주기 때문입니다.
이런 객체는 이미 액션마다 다른 고유 식별자와 함께 type 속성을 가지고 있습니다.
자동으로 처리되기 때문에 액션 식별자에 대해 신경 쓸 필요가 없습니다.
5️⃣ export const counterActions = counterSlice.actions;
부분입니다.
counterSlice.action.REDUCER_METHOD
식으로 접근하면 됩니다.기존 리덕스와 비교하였을 때, 리덕스 툴킷을 사용하하여 액션 생성자로 액션 객체를 생성함으로 얻는 이점이 있다.
이전 게시물_ Redux State를 올바르게 사용하는 방법을 읽고 오면 redux-toolkit이 얼마나 편한지 이해될 수 있습니다.