Redux Toolkit은 Redux의 사용을 더 쉽게 하기 위해 Redux에서 공식 제공하는 개발도구이다. 그에 따라 Redux Toolkit은 Redux에 대한
라는 세 가지 일반적인 문제를 해결하기 위해 만들어졌다.
기존의 redux는 1개의 액션을 생성할 시 액션타입 정의 -> 액션함수 생성 -> 리듀서 정의 의 작업이 필요하다. 또 이 과정에서 필요에 의해 다양한 라이브러리가 사용이 되는데,
많아지는 액션을 관리하기 위한 redux-actions, 불변성을 지키기 위한 immer, 비동기 작업 수행을 위한 redux-thunk, redux-saga등 리덕스의 여러 기능 사용을 위해 여러 라이브러리를 사용해야 했다. 하지만 redux-toolkit은 redux-saga를 제외한 위의 모든 기능을 지원한다.
또, Typescript 사용자를 위해 action type, state type 등 Typescript 를 사용하며 필요한 Type Definition 까지 공식 지원한다.
npm install @reduxjs/toolkit react-redux
npm install @reduxjs/toolkit
개인적으로 프로젝트를 진행할 때,
src/redux/configStore.ts 파일을 생성해서 Redux 저장소를 만들어줬다.
import { configureStore } from "@reduxjs/toolkit";
import productSlice from "./modules/productSlice";
import userSlice from "./modules/userSlice";
import cartSlice from "./modules/cartSlice";
export const store = configureStore({
reducer: {
},
});
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch;
export default store;
이렇게 설정된 Redux 스토어는 애플리케이션의 전역 상태 관리를 담당하며, store 변수를 통해 액션 디스패치(dispatch) 및 상태 조회 등의 기능을 사용할 수 있습니다. 리덕스 스토어를 설정함으로써 애플리케이션의 상태 관리를 편리하게 구현할 수 있다.
// src/hooks/reduxHooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "../redux/configStore";
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
이러한 커스텀 훅들을 사용하면 Redux 스토어의 dispatch 메서드와 상태를 간편하게 사용할 수 있습니다. useAppDispatch를 사용하여 액션을 디스패치하고, useAppSelector를 사용하여 상태를 가져올 수 있다.
// index.tsx
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import store from "./redux/configStore";
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</>
);
// src/redux/modules/productSlice.ts
import { Product, ProductDetail } from "../../components/types/product";
import { apis } from "../../shared/api";
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
interface PropductInitProps {
productOne: ProductDetail | null;
}
const initialState: PropductInitProps = {
productOne: null
}
// 하나의 상품 가져오기
export const getOneProduct = createAsyncThunk(
"product/getOneProduct",
async (productId: number, thunkAPI) => {
try {
const res = await apis.getOneProduct(productId);
return thunkAPI.fulfillWithValue(res.data)
} catch (error) {
console.log("상품하나", error);
throw error;
}
}
)
const productSlice = createSlice({
name: 'product',
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getOneProduct.pending, (state) => {})
.addCase(getOneProduct.fulfilled, (state, action) => {
state.productOne = action.payload;
})
.addCase(getOneProduct.rejected, (state) => {})
}
})
createAsyncThunk를 사용하여 생성한 액션 생성자 함수는 비동기 작업을 수행하고, 작업이 진행 중인지, 성공했는지, 실패했는지에 따라 해당하는 액션을 자동으로 디스패치한다.
이를 통해 비동기 작업의 상태를 관리하고, 상태 업데이트에 따른 UI 변경을 처리하는 데 편리하게 사용할 수 있다.
import { configureStore } from "@reduxjs/toolkit";
import productSlice from "./modules/productSlice";
export const store = configureStore({
reducer: {
product: productSlice,
},
});
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch;
export default store;
const dispatch = useAppDispatch()
const product = useAppSelector((state) => state.product.productOne)
useEffect(() => {
dispatch(getOneProduct(finalId))
}, [dispatch, finalId])
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
export function Counter() {
// 데이터 추출
const count = useSelector((state) => state.counter.value)
// 액션 추출
const dispatch = useDispatch()
return (
<div>
<div>
<button
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
)
}
출저