요즘 핫한 react-query와 swr에 영향을 받아서 인지 redux보다 사용하기 편한 redux-toolkit을 만들었습니다. redux-toolkit 기본 사용법을 익히고자 생활코딩 redux-toolkit 강의를 보면서 학습하였습니다.
- 초기 설정이 간편해졌습니다. 기존 redux는 리덕스 스토어를 구성하는 것은 너무 복잡하였지만, 툴킷에서는 좀 더 간편화되었습니다.
- 더이상 다양한 패키지들를 설치 하지 않아도 됩니다. 리덕스를 사용하면 redux devtool, immer, thunk 등 여러가지 라이브러리를 추가적으로 설치해야 하지만, redux-toolkit 내부에 이미 설치가 되어 있기에 굳이 설치 할 필요가 없습니다.
- 반복되는 코드가 너무 많아 코드가 복잡해지고 실수를 많이 유발했지만 이러한 부분이 많이 개선되었습니다.
- 툴킷에서는 더이상 불변성을 신경쓰지 않아도 됩니다.
학습을 위해 리덕스를 활용해 카운팅 프로젝트를 만들었으며 파일 하나하나씩 보여드리면서 설명을 하겠습니다.
Redux + Plain JS template
npx create-react-app my-app --template redux
Redux + TypeScript template
npx create-react-app my-app --template redux-typescript
NPM
npm install @reduxjs/toolkit
Yarn
yarn add @reduxjs/toolkit
import { Provider } from "react-redux";
import Counter from "./components/Counter";
import store from "./store";
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
export default App;
이부분은 기본 리덕스와 비슷한데 Provider로 store를 저장해줍니다.
store는 저장소라고 생각하시면 이해하기 쉽습니다.
그 저장소 내부에는 여러 개의 slice들로 이루어져 있습니다.
import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "./counterSlice";
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
먼저 store에 대해 설명드리자면 configureStore를 통해 store를 만들어주었습니다.
configureStore의 역할은 여러 개의 slice들을 모아주는 역할이라고 보면 이해하기 쉽습니다.
내부 reducer에 s를 붙이지 않게 주의합니다.
타입스크립트를 사용 중이기에 RootState, AppDispatch를 만들어주었습니다.
import { createSlice } from "@reduxjs/toolkit";
type InitialState = {
id: number;
value: number;
};
const initialState: InitialState = {
id: 1,
value: 0,
};
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
up: (state, action) => {
state.value = state.value + action.payload;
},
down: (state, action) => {
state.value = state.value - action.payload;
},
init: (state, action) => {
state.value = 0;
},
},
});
export default counterSlice;
export const { up, down, init } = counterSlice.actions;
createSlice로 Slice를 만듭니다. createSlice는 객체를 인자로 받고 객체에는 name, initialState, reducers를 필수 요소입니다.
name: 이름, initialState: 초기 상태, reducers: 메소드(함수)로 이루어져 있습니다.
counterSlice는 store/index.tsx (저장소)에서 사용도 하지만, dispatch 할때도 사용해야 하기에 export를 해줍니다.
dispatch에서 메소드에 접근하기 쉽게 구조분해 할당을 해줍니다.
import { useSelector, useDispatch } from "react-redux";
import { RootState } from "../store";
import { down, init, up } from "../store/counterSlice";
function Counter() {
const dispach = useDispatch();
const count = useSelector((state: RootState) => {
return state.counter.value;
});
const addNumber = () => {
dispach(up(2));
};
const minusNumber = () => {
dispach(down(2));
};
const initNumber = () => {
dispach(init(""));
};
return (
<div>
<div>{count}</div>
<button onClick={addNumber}>+</button>
<button onClick={minusNumber}>-</button>
<button onClick={initNumber}>초기화</button>
</div>
);
}
export default Counter;
해당 컴포넌트에는count의 값을 나타내기 위해 redux state에서 데이터를 받아와 보여주고 총 3가지 버튼으로 더하기,빼기,초기화 기능이 있습니다.
useSelector 는 store의 state를 인자를 받아옵니다 (큰 저장소).
즉, console.log(state)로 찍어보면 내부에 counter(counterSlice)를 가지고 있는 걸 알 수있습니다. 만약 slice가 더 있다면 여러 개가 나타겠죠?
우리는 counter가 가지고 있는 value값에 접근하여 이를 화면에 출력합니다.타입스크립트를 사용하시는 분은 state 타입을 RootState로 설정해주면 됩니다.
그러면 counterSlice/initialState타입으로 설정됩니다.RootState와 AppDispatch는 주로 hook로 설정해서 사용하기도 합니다.
다음으로 useDispach에 대해 설명드리자면, dispach를 통해 counterSlice의 내부 메소드에 접근할 수 있습니다.
우리는 메소드를 구조분해할당으로 빼뒀기 때문에 이런 식으로 dispach(up(2));
바로 사용이 가능합니다.
counterSlice의 up메소드에서 console.log(action)
을 찍어보면 인자 값인 2가 action.payload로 있는 걸 확인할 수 있습니다.