RTK
Redux를 편하게 사용하기 위한 리덕스 공식 개발 도구이다.
RTK는 saga를 제외한 나머지 라이브러리를 따로 설치하지 않고 사용할 수 있게 해준다.
라이브러리 종류
- redux-actions : 많아지는 액션을 관리
- immer : 상태값의 불변성 보존
- reselect : store값을 효율적으로 핸들링하여 불필요한 리렌더링을 막아줌
- redux-thunk : 액션을 비동기적으로 만들 수 있음
- redux-saga
# toolkit 설치
npm install @reduxjs/toolkit react-redux
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
/* Provider 가져오기 */
import { Provider } from "react-redux";
import store from "./store/count";
ReactDOM.createRoot(document.getElementById('root')).render(
<Provider store={store}>
<App />
</Provider>
)
combineReducers로 리듀서들을 모아두지 않고,
reducer속성 안에 리듀서 함수를 넣어주면 된다.
/* configureStore 가져오기 */
import { configureStore } from "@reduxjs/toolkit";
import { counterSlice } from "../reducers/countReducer";
import { logger } from "redux-logger";
...
export const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
// 사용할 미들웨어 배열
// 기본 미들웨어를 무시하고 사용자 정의 미들웨어만 적용하고 싶다면 ▹ middleware: [thunk, logger],
middleware : (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
// 개발자 도구를 사용할 것인지(boolean) 디폴트 값은 true이다.
devTools : process.env.NODE_ENV !== 'production',
// 초기 state
preloadedState,
// enhancer 배열(middleware가 적용되는 순서보다 앞서서 추가하고 싶을 때 사용)
enhancers : [batchedSubscribe, ...defaultEnhancers],
});
createSlice는 액션에 대한 함수 설정과
리듀서를 따로 생성하지 않아도 된다.
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
name: "TATA",
value: 0,
};
export const counterSlice = createSlice({
name: 'counter', // 이름
initialState, // 초기값
reducers: { // 리듀서는 액션 하나하나 모여서 구성
increase: (state) => {
state.value++;
},
decrease: (state) => {
state.value--;
},
changeName: (state, action) => {
state.name = action.payload;
},
changeAll: (state, action) => {
return {...state, ...action.payload};
},
},
})
// action 내보내는 방법
export const { increase, decrease, changeName } = counterSlice.actions;
// slice는 slice.reducer로 내보낸다.
export default counterSlice.reducer;
useSelector
: 스토어에서 현재 상태 값을 가져오기
useDispatch
: 변경되는 값을 스토어로 전달
/* Count.js */
import { useSelector, useDispatch } from "react-redux";
import { decrease, increase, changeName, changeAll } from "../reducer/counter";
const Count = () => {
const { name, value } = useSelector((state) => state.counter);
const dispatch = useDispatch();
const changeNameFunc = () => {
dispatch(changeName("Chimmy"));
};
const changeAllFunc = () => {
dispatch(changeAll({ name: "BT21", value: 7 }));
};
return (
<>
<h2>Name {name}</h2>
<span>Count: {value}</span>
<button onClick={() => dispatch(increase())}>+</button>
<button onClick={() => dispatch(decrease())}>-</button>
<button onClick={changeNameFunc}>changeName</button>
<button onClick={changeAllFunc}>changeAll</button>
</>
);
};
export default Count;
✚ 비동기 작업 함수 작성
기존의 react-thunk 사용 방식으로는 slice로 구현한 state를 변경할 수 없다.
toolkit에서 제공하는 createAsyncThunk로 비동기 작업을 구현해야 한다.
createAsyncThunk(type, payloadCreator, options)
/*
* type: 해당 요청의 타입
* (pending, fulfilled, rejected가 알아서 상황에 맞게 붙여짐)
*
* pending: 대기
* fulfilled: 이행 => payload로 보내짐
* rejected: 실패 => action.error로 보내짐
*
*------------------------
*
* payloadCreator: 비동기 함수 실행 부분(인자 두개를 받음)
*
* arg: 첫번째 파라미터(생략가능)
* 실행하고자 하는 비동기 함수를 구성하는데 사용될 사용자 입력으로 활용
*
* thunkAPI: dispatch, getState, rejectWithValue,
* fulfillWithValue 등의 함수를 실행 할수 있는 API 묶음
*/
/* createAsyncThunk 가져오기 */
import { createAsyncThunk } from "@reduxjs/toolkit";
// thunk 함수 만들기
const userThunk = createAsyncThunk(
"user/userThunk",
async (thunkAPI) => {
try {
const res = axios.get("https://..../user");
return res.data;
} catch (error) {
return thunkAPI.rejectWithValue(error);
}
});
Redux에서는 자체적으로 비동기 처리를 지원하지 않아서,
extraReducers를 사용해 Thunk함수를 등록시켜주어야 한다.
const initialState = {
loaging: false,
data: [],
error: null,
}
export const userSlice = createSlice({
name: "userReducer",
initialState,
extraReducers: (builder) => {
builder
.addCase(userThunk.pending, (state, action) => {
state.loading = true;
})
.addCase(userThunk.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(userThunk.rejected, (state, action) => {
state.loading = false;
state.error = action.error;
})
}
})
/*
* extraReducer에 파라미터인 builder를 통해
* addCase로 case(pending, fulfilled, rejected)를 등록시켜준다.
*/
☂️ Redux로 전역상태 관리하는 방법 보러가기
👉 React Toolkit 잘 정리된 블로그1
👉 Redux Toolkit 잘 정리된 블로그2
👉 Redux Toolkit 잘 정리된 블로그3
👉 (공식문서)configureStore + createReducer + createSlice + createAsyncThunk