react 상태를 한곳에서 관리를 하기 위해서 redux, mobx, recoil과 같은 라이브러리를 사용하면 한곳에서 상태를 관리 할 수 있다. 하지만 redux 라이브러리를 사용하기 위해서는 store, reducer, action을 셋팅을 해서 사용을 해야한다. 그래서 오리려 작은 어플리케이션 같은 경우는 셋팅하는 공수가 더 큰 번거로움이 있다.
이를 보완하기 위해 redux-toolkit이 나왔고, 이전에 했던 redux 셋팅에 비해 간소해졌다. 최근 회사에서 redux를 사용하게 되면서 redux를 설치했더니, redux 에서 redux-toolkit을 권유하게 되었고 기존 셋팅에 비해 간소화 된 걸 확인 할 수 있었다.
redux를 사용하면 redux-thunk, redux-sage 아니면 rxjs 같은 미들웨어가 필요했는데 redux toolkit은 기본적으로 redux-toolkit을 내장하고 있다(셋팅할 때, 다른 미들웨어를 셋팅할 수 있다.)
# object
- redux toolkit 사용해서 상태 관리(slice)
- redux toolkit api 통신(extra reducer)
## Redux-toolkit setting
* next-redux-wrapper도 버전 업이 되면서 이전 셋팅 방식과 달라짐
// 버전 7 미만일땐
export default wrapper.withRedux(MyApp)
// 버전 7 이상
const MyApp = ({pageProps}) => {
const { store, props } = wrapper.useWrappedStore(pageProps);
//...
}
export default MyApp
// store/index.ts
import { configureStore } from "@reduxjs/toolkit";
import { rootReducer } from "../reducer";
import { createWrapper } from "next-redux-wrapper";
const makeStore = () => {
const store = configureStore({ reducer: rootReducer });
return store;
};
const store = makeStore();
const wrapper = createWrapper(makeStore);
export default wrapper;
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// reducer/index.ts
import { AnyAction, combineReducers } from "@reduxjs/toolkit";
import { HYDRATE } from "next-redux-wrapper";
import mainReducer from "./mainSlice";
const combinedReducers = combineReducers({
main: mainReducer,
});
export const rootReducer = (state: any, action: AnyAction) => {
switch (action.type) {
case HYDRATE:
return { ...state, ...action.payload };
default:
return combinedReducers(state, action);
}
};
// _app.tsx
import type { AppProps } from 'next/app';
import wrapper from '../redux/store';
import { Provider } from 'react-redux';
function App({ Component, pageProps }: AppProps) {
const { store, props } = wrapper.useWrappedStore(pageProps);
return (
<Provider store={store}>
<Component {...props} />
</Provider>
);
}
export default App;
## Redux-toolkit 사용
* slice 라는 새로운 메소드가 나왔다.
* 이전에 action type과 reducer 함수를 따로 관리를 했지만, slice 한곳에서 같이 관리 할 수 있다.
* createSlice를 통해서 slice를 만든다.
* slice를 판별하는 name: key (unique name) - required
* 상태 초기값 - required
* reducer - reducer 함수
* extraReducers - thunk 함수
* slice를 통해서 actions와 reducer를 export
import { createSlice } from "@reduxjs/toolkit";
const nodeSlice = createSlice({
name: "nodeSlice",
initialState,
reducers: {
changeNodePage(state, action) {
state.currentPage = action.payload;
},
changeNodeNextPage(state, action) {
state.pageNo = action.payload;
},
},
extraReducers: {},
});
export const { changeNodePage, changeNodeNextPage } = nodeSlice.actions;
export default nodeSlice.reducer;
### 실제 사용 코드
* createAsyncThunk를 통해서 api 통신
* createAsyncThunk 첫번째 인자로 unique key 값 필요
* 두번째 인자로 async callback 함수
* dispatch(getNode({baseUrl, query})에서 getNode 인자가 thunk callback 인자로 들어온다
callback 인자를 사용할 때 주의 할 점은 파라미터를 1개를 받는다(이걸로 헛짓많이함) 그래서 기본적인 string이나 number를 보낼 수 있고, 여러 인자를 보내려고 할떄는 객체로 만들어서 보내야한다.
* getNode의 return 값이 extraReducer의 action.payload로 들어온다.
* fullfilled는 통신 성공 / rejected는 error 발생
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
import { makeQueryString } from "../../utils";
interface IProps {
baseUrl: string;
query: IQuery;
}
interface IQuery {
page_no: number;
offset: number;
}
const initialState = {
result: null,
error: null,
pageNo: 1,
offset: 10,
currentPage: 1,
};
export const getNode = createAsyncThunk(
"getNode",
async ({ baseUrl, query }: IProps) => {
try {
const queryString = makeQueryString(query);
const response = await axios.get(`${baseUrl}/api/node?${queryString}`);
if (response.status === 200) {
return response.data;
}
} catch (error) {
return error;
}
}
);
const nodeSlice = createSlice({
name: "nodeSlice",
initialState,
reducers: {
changeNodePage(state, action) {
state.currentPage = action.payload;
},
changeNodeNextPage(state, action) {
state.pageNo = action.payload;
},
},
extraReducers: {
[getNode.fulfilled.type]: (state, action) => {
state.result = action.payload;
},
[getNode.rejected.type]: (state, action) => {
state.result = initialState.result;
state.error = action.error;
},
},
});
export const { changeNodePage, changeNodeNextPage } = nodeSlice.actions;
export default nodeSlice.reducer;
// 사용 예시
const dispatch = useDispatch();
dispatch(changeNodePage(1))
await dispatch(getNode({baseUrl: 'http://localhost:3000', query}))
'Coding > Redux' 카테고리의 다른 글
Redux - Pagnigation(with redux-toolkit and nextjs) (0) | 2023.02.09 |
---|---|
Redux - next.js store에 접근 (2) | 2021.10.16 |
Redux- search page Scroll 제어하기(무한 scroll down) (0) | 2021.04.04 |
Redux - immutable - Next tutorial(마무리)(수정본) (0) | 2021.02.07 |