TIL

Redux

Redux-thunk

떵크가 뭔데 사용할까?

redux-thunk가 없는 경우의 예제 코드

<button
  onClick={async () => {
    const response = await fetch('https://api.countapi.xyz/visits');
    const data = await response.json();
    dispatch(set(data.value));
  }}
>
  async button without thunk
</button>

redux-thunk를 사용한 예제 코드

<button
  onClick={() => {
    dispatch(asyncUpFetch());
  }}
>
  async button with thunk
</button>

기존: 비동기 작업의 결과 데이터를 redux store에 dispatch함
추가 후: 비동기 작업을 reducer에 포함시켜 동작할 수 있게 해 유지보수에 용이


createAsyncThunk()

createAsyncThunk는 비동기 작업을 처리하는 액션을 만들어준다.
액션 타입과 액션이 생성될 때 작업할 비동기 함수를 parameter로 받음.

const asyncUpFetch = createAsyncThunk('counterSlice/asyncUpFetch', async () => {
  const response = await fetch('https://api.countapi.xyz/visits');
  const data = await response.json();
  return data.value;
});


비동기 작업은 extraReducers 사용
-> redux-tool-kit에서 reducers는 action creator를 자동 생성해주지만, 비동기 작업은 자동 생성되지 않아 extraReducers에 직접 정의 필요
builder.addCase()로 비동기 작업 상태와, 각 작업 상태일 때 실행할 콜백 함수를 각각 파라미터로 전달함

const counterSlice = createSlice({
  name: 'counterSlice',
  initalState: {
    value: 0,
    status: 'Welcome',
  },
  reducers: {
    up: (state, action) => {
      state.value += action.payload;
    },
  },
  extraReducers: builder => {
    builder.addCase(asyncUpFetch.pending, (state, action) => {
      state.status = 'Loading';
    });
    builder.addCase(asyncUpFetch.fulfilled, (state, action) => {
      state.value += action.payload;
      state.status = 'complete';
    });
    builder.addCase(asyncUpFetch.rejected, (state, action) => {
      state.status = 'fail';
    });
  },
});


Redux-persist

case) state 객체 내 일부 프로퍼티만 persist 적용하는 경우

  1. 각각의 reducer로 persisted reducer 생성

persist config에는 whitelist, blacklist라는 프로퍼티가 있음
whitelist는 persist할 리스트를 명시할 때, blacklist는 persist에 제외할 리스트를 명시할 때 사용

import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { userReducer } from './userSlice';
import { cartReducer } from './cartSlice';

const userPersistConfig = {
  key: 'user',
  storage, // use localStorage to persist
  whitelist: ['userInfo'], // Only persist the "userInfo" property
};

const cartPersistConfig = {
  key: 'cart',
  storage,
  whitelist: ['cart'], // Only persist the "cart" property
};

const persistedUserReducer = persistReducer(userPersistConfig, userReducer);
const persistedCartReducer = persistReducer(cartPersistConfig, cartReducer);
  1. persisted reducers를 combine하여 rootReducer 생성
import { combineReducers } from '@reduxjs/toolkit';

const persistedUserReducer = persistReducer(userPersistConfig, userReducer);
const persistedCartReducer = persistReducer(cartPersistConfig, cartReducer);
  1. rootReducer로 store 생성
import { configureStore } from '@reduxjs/toolkit';

const store = configureStore({
  reducer: rootReducer,
});

export default store;

직렬화를 이유로 순서를 되도록 지키라고 되어 있는데, 직렬화에 대한 내용도 잘 이해가 안되고, 순서에 대한 내용도 찾기 어려웠다.
여러 케이스로 테스트한 결과 persist+configure+(combine)를 함께 사용할 때 이 순서로 했을 때 config도 지켜지고, persist도 잘 동작하며 문제가 없었다.