Redux Toolkit

chu·2021년 3월 22일
4

이번 시간에는 redux toolkit에 대해서 정리를 해보려고 한다.

Reudx Toolkit?

리덕스 공식팀에서 그 동안 많이 사용된 기능을 모아 만든 라이브러리이다.

그 동안 redux-thunk immer redux-saga 등 복잡하고,
많은 코드를 작성하는 부분을 확 줄일 수 있게 됀 부분이라 생각하면 됀다.

install

npm i @reduxjs/toolkit

store

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’,
});

combineReducers

import { combineReducers } from 'redux'
import userReducer from './user';
import postReducer from './post';

// 리듀셔에 .reducer를 붙혀서 내보내야한다.
exports default = combineReducers({
  user: userReducer.reducer,
  post: postReducer.reducer,
})

대표적으로 가장 많이 쓰이는 기능 위주로 정리해보자.

createSlice

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));
},[])

extraReducers

추후 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에서는 자동으로 지어준다. 아래 예시로 확인해보자.

예시를 바로 보여주기 전에 추가적으로 기능을 하나더 소개하겠다.

createAsyncThunk

리덕스에서 비동기 처리할 때 쓰이는 그 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으로 작업해보자.

profile
한 걸음 한걸음 / 현재는 알고리즘 공부 중!

0개의 댓글