[redux-toolkit]
리액트로 진행한 프로젝트에서 리덕스를 공부한 적이 있는데
리덕스의 보일러 플레이트 코드를 간소화하기 위해 리덕스 툴킷 (redux-toolkit, rtk)를 사용한 적이 있다.
공부하면서 노션에 정리해놓은 것을 다시 복기할 겸 정리하려고 한다.
<STUDY>
- 상태 관리 라이브러리 (state management library) → redux, modox, recoil 등
- state가 상위 component로 올라갈수록 관리하기 복잡해지는 현상 해결
Redux toolkit (RTK)
- Redux 로직을 작성하고 저장소를 설정하는 프로세스(redux 작업)를 단순화
- redux 저장소 구성의 복잡성, 많은 패키지 추가, 상용구 코드 해결 위해 사용하는 도구
- API
- configureStore() : 단순화된 구성 옵션과 기본값을 제공(redux store설정)
- createSlice() : 초기 상태, 리듀서 함수의 객체, 슬라이스 이름을 받아 리듀서 및 상태에 해당하는 액션 생성자와 액션 유형을 자동으로 생성하는 함수 → redux 로직 작성 위한 표준 접근 방식
💡 설치
npm install @reduxjs/toolkit react-redux
or
yarn add @reduxjs/toolkit
💡 사용 방법
1. RootState, AppDispatch정의
type이기 때문에 store 파일에서 직접 export (따로 파일을 만들 필요 x)
// store.ts
import { configureStore } from '@reduxjs/toolkit';
export const store = configureStore({
reducer: {}
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
2. useSelector, usedispatch를 hook으로 정의 → useAppSelector, useAppDispatch
- type이 아닌 실제 변수(variable) 이므로 hooks.ts 같은 별도의 파일에 정의해야 한다.
- type을 컴포넌트마다 임포트 하는 횟수를 줄여줌
// hooks.ts
import { useDispatch, useSelector } from "react-redux";
import type { TypedUseSelectorHook } from "react-redux";
import type { RootState, AppDispatch } from "./store";
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
❗ useAppSelector를 따로 정의해야 하는 이유
- useSelector를 그대로 사용하는 경우 타입을 항상 명시해 주어야 하기 때문!
// useAppSelector 정의한 경우
import { useAppSelector } from '../app/hooks'
const count = useAppSelector(state => state.counter.count);
// useAppselector 정의하지 않은 경우
import { useSelector } from 'react-redux';
import { RootState } from '../modules';
const count = useSelector((state: RootState) => state.counter.count); // (state: RootState) 항상 써줘야함
3. store 추가
// index.tsx
import { Provider } from 'react-redux';
import { store } from './app/store';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<Provider store={store}>
<App />
</Provider>
);
4. reducer 생성 및 추가
- Slice 정의
// countSlice.ts
import { createSlice } from "@reduxjs/toolkit";
interface menuState {
value: number;
};
const InitialState: menuState = {
value: 0,
};
export const CountSlice = createSlice({
// state의 string name (보통 action type 상수로 사용함)
name: 'count',
// 초기 state 값 (reducer가 undefined로 호출될 때마다 사용)
initialState: InitialState,
// 특정 action type을 처리하기 위한 기능을 포함하는 객체
reducers: {
plus: (state) => {
state.value++;
},
minus: (state) => {
state.value--;
},
}
});
export const { plus, minus } = CountSlice.actions;
export default CountSlice.reducer;
- store에 reducer로 전달
// store.ts
import { configureStore } from '@reduxjs/toolkit';
import CountReducer from './reducer/countSlice';
export const store = configureStore({
reducer: {
count: CountReducer,
}
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
💡 redux-toolkit을 사용하면 reducer에서 "mutating" 로직을 작성할 수 있다.
📌 redux-toolkit은 Immer 라이브러리를 사용하고 있어 실제로 상태를 변경하지 않는다. (불변성)
📌 Immer 라이브러리는 "draft state"의 변경 사항을 감지하고 이러한 변경 사항을 기반으로 새로운 불변 상태를 생성하기 때문에 불변성을 유지할 수 있다.
5. 컴포넌트에서 사용하기
import {useAppDispatch, useAppSelector} from './app/store';
import {plus, minus} from './app/reducer/counterSlice';
export default Component1() {
const dispatch = useAppDispatch(); // dispatch 임포트
const count = useAppSelector(state => state.count.value); // count state 임포트
return (
<>
<div>{count}</div>
<div>
<button type='button' onClick={() => dispatch(plus())}>
plus
</button>
<button type='button' onClick={() => dispatch(minus())}>
minus
</button>
</div>
</>
)
}
HNTECH 홈페이지 프로젝트를 하면서 상태관리도구(Redux)를 처음 사용해 보았다.
리덕스에 대한 자료를 찾아보면서 따로 설정해야 하는 부분도 많고 꽤 복잡하다는 생각이 들었는데
redux-toolkit을 통해 보일러플레이트를 확실히 줄일 수 있고 사용법도 간편해서 훨씬 편하게 느껴졌던 기억이 난다.
특히 redux-toolkit이 Immer 라이브러리를 사용하기 때문에 mutating 로직을 사용할 수 있다는 사실을 처음 알게되었다.
공부했던 내용을 다시 볼수록 새로운 내용을 알게 되는 것 같아서 즐거웠다 ㅎㅎ
공식 문서를 조금 더 자세히 읽어보면서 더 효율적으로 사용할 수 있는 방법도 고민해 봐야겠다!
<자세한 내용은 redux-toolkit 공식 홈페이지를 확인해 주세요>
'RECORD' 카테고리의 다른 글
[Record] 신입 프론트엔드 지망생의 취업 성공기_ (7) | 2023.08.05 |
---|---|
[Firebase] 인증 구현하기 / 이메일 비밀번호, 구글 로그인 (0) | 2023.06.12 |
[바닐라 자바스크립트] 프로젝트 초기 환경 설정 / Webpack, Babel, Vanilla Javascript (0) | 2022.12.03 |
[환경설정] webpack.config.js 오류 / require, CommonJS (0) | 2022.11.30 |
[Vite] 환경변수 사용하기 (0) | 2022.11.20 |