이번 시간에는 redux toolkit에 대해서 정리를 해보려고 한다.
리덕스 공식팀에서 그 동안 많이 사용된 기능을 모아 만든 라이브러리이다.
그 동안 redux-thunk
immer
redux-saga
등 복잡하고,
많은 코드를 작성하는 부분을 확 줄일 수 있게 됀 부분이라 생각하면 됀다.
npm i @reduxjs/toolkit
import { configureStore, getDefaultMiddleware } from ‘@reduxjs/toolkit’;
import combineReducers from '../reducers/index';
const reducer = combineReducers;
const store = configureStore({
reducer,
middleware: […getDefaultMiddleware()],
devTools: process.env.NODE_ENV !== ‘production’,
});
import { combineReducers } from 'redux'
import userReducer from './user';
import postReducer from './post';
// 리듀셔에 .reducer를 붙혀서 내보내야한다.
exports default = combineReducers({
user: userReducer.reducer,
post: postReducer.reducer,
})
대표적으로 가장 많이 쓰이는 기능 위주로 정리해보자.
import { createSlice } from ‘@reduxjs/toolkit’;
initailState = {
isLogin = false, // boolean
data = null, // string
}
// 내부에서는 자동으로 immer가 작동됀다.
const userSlice = createSlice({
name: ‘user’,
initialState,
reducers: { // 내부 action 및 동기 action
logIn((state, action) => {
state.isLogin = true;
state.data = action.payload;
}),
},
extraReducers: { // 외부 action 및 비동기 action
},
});
reducers
는 내부에서 진행되는 action 및 동기 action을 넣는 공간이며, 이렇게 작업됀 reducer는 추후 dispatch 할 경우에 아래처럼 실행해야한다.
import { userSlice } from '../reducers/index'
const onLogIn = useCallback(() => {
dispatch(userSlice.actions.logIn(...data));
},[])
extraReducers
는 반대로 외부/비동기 action을 넣으면 됀다.
이 때는 아래처럼 실행하면 됀다.
import { logIn } from '../reducers/index'
const onLogIn = useCallback(() => {
dispatch(logIn(...data));
},[])
추후 API로 비동기 작업을 진행할 때에는 요청/성공/실패 이 세가지로
나눠지게 되는데, redux-toolkit에는 이 세가지에 대해서 명확하게 정해져있다.
pending
- 요청
fulfilled
- 성공
rejected
- 실패
어떻게 사용될까?
const userSlice = createSlice({
name: ‘user’,
initialState,
reducers: { // 내부 action 및 동기 action
},
extraReducers: { // 외부 action 및 비동기 action
[logIn.pending](state, action) { // 요청
state.isLogin = false;
},
[logIn.fulfilled](state, action) { // 성공
state.isLogin = false;
state.data = action.payload;
},
[logIn.rejected](state, action) { // 실패
state.isLogin = false;
state.data = null;
},
},
});
위 처럼 세가지에 대한 작업을 action마다 생성을 해야한다.
이렇게 보면 그 전에 진행했던 redux에서 reducer에 switch case문이 생각난다.
또한 action을 생성하는 경우 변수명도 지었다.
ex) ADD_POST_REQUEST, ADD_POST_SUCCESS, ADD_POST_FAILURE
하지만 redux-toolkit에서는 자동으로 지어준다. 아래 예시로 확인해보자.
예시를 바로 보여주기 전에 추가적으로 기능을 하나더 소개하겠다.
리덕스에서 비동기 처리할 때 쓰이는 그 thunk다.
import { createAsyncThunk } from '@reduxjs/toolkit';
const logIn = createAsyncThunk('user/logIn', async(data, thunkAPI) => {
const result = await axios.get(...);
return result;
})
user/logIn
이 부분이 바로 action의 이름이다.
바로 이 이름으로 변수명이 자동으로 만들어진다.
ex) user/logIn/pending, user/logIn/fulfilled, user/logIn/rejected
위 부분은 devTools에서도 확인이 가능하다.
data
에서는 호출할 때 전달 됀 데이터가 담아져 있다.
ex) 로그인 관련 id, password가 담아져 있을거다.
thunkAPI
는 getState()를 통해 state도 알 수 있다.
하지만 정확한 사용에 대해서는 아직 미숙하기 때문에 추후 작업을 통해 알아보자.
물론 이 외에도 createAtion
, createReducer
도 있지만,
이제는 어딜봐도 typescript는 기본이다. 입문 강의는 예전에
들어봤지만 아직 작업을 통해 실제로 사용한적은 없다.
갑자기 왠 타입추론을 얘기하는지 의문을 가질 수 있다.
아래 내용을 통해 간단하게 하나만 소개하겠다.
// 기존 API 호출
extraReducers: {
[logIn.pending](state, action) { // 요청
state.isLogin = false;
},
[logIn.fulfilled](state, action) { // 성공
state.isLogin = false;
state.data = action.payload;
},
[logIn.rejected](state, action) { // 실패
state.isLogin = false;
state.data = null;
},
},
// 정확한 타입추론을 위한 addCase
extraReducers: (builder) => builder
.addCase(logIn.pending, (state, action) => {
state.isLoggingin = true;
}),
.addCase(logIn.fulfilled, (state, action) => {
state.isLoggingin = false;
state.data = action.payload;
}),
.addCase(logIn.rejected, (state, action) => {
state.isLoggingin = true;
state.data = null;
}),
.addMatcher((action) => { // action별 공통업무
return action.type.includes(‘/pending’);
}, (state, action) => {
// 업무
}),
.addDefault((state, action) => {
// default
}),
위 addCase를 사용하면 보다 더 타입에 대한 추론이 가능하다고 한다.
추가적으로 소개하는 것이니 더 알아보고 사용하면 좋겠죠?
더군다나 addmatcher
addDefault
는 redux에서 reducer를
만들때도 사용했던 부분이다.
addmatcher
아래 예시처럼 여러 상황이지만, 같은 업무를 처리할 때 사용하면 됀다.
switch (type, action) {
case ADD_POST_SUCCESS:
case LOG_IN_SUCCESS:
break;
}
.addMatcher((action) => { // action별 공통업무
return action.type.includes(‘/pending’); // pending 경우
}, (state, action) => {
// 업무...
}),
addDefault
는 따로 설명없이 switch문에서 default이다.
정리를 하면서 다른 블로그 및 인프런 강의를 들으면서
설명해주는것도 없는것도 각각 달랐다.
정확한 설명은 당연히 공식문서에 있겠지만...
다른 블로그를 확인하고, 작업을 해보면서 익히는게 가장 좋을 것 같다.
이 후로는 작업했던 TodoApp을 redux-toolkit으로 작업해보자.