본문 바로가기
  • 삽질하는 자의 블로그
Redux, Redux-toolkit

10. [ Redux-toolkit ] createSelector !! memoization된 값을 반환한다

by 이게뭐당가 2023. 1. 25.

Store 에서 오는 객체값이 너무나 커서, 리렌더링 될때마다 새 객체값을 생성시키고, 엄청난 양의 메모리를 소모하게 되었다.

 

그러므로, 평소처럼 useMemo 를 사용하여, memoizition 을 하는 방향을 모색하려는데, 나는 Redux-toolkit 을 사용한다!

 

Redux-toolkit 에는, 메모이제이션 을 미리 사용하여, 내보낼 수 있는 좋은 기능이 있다.

 

바로 createSelector 이다.

 

https://react-redux.js.org/api/hooks#using-memoizing-selectors

 

Hooks | React Redux

API > Hooks: the `useSelector` and `useDispatch` hooks`

react-redux.js.org

 

createSelector 는 기존의 useSelector 를 사용할때, 리렌더 될때마다 계속해서 새 인스턴스가 생성되는 것을 방지하고

 

memoization 을 사용하여 기존의 값을 재사용 할 수 있게 만들어줍니다.

 

또한, 기존의 useSelctor 에서 가져올때, 1차적으로 가공하여 밖으로 빼오는 함수를 사용하는 것 처럼

 

createSeletor 안에서, 1차적인 가공을 미리하여, 필요한 값만 빼 올 수 있도록 만들 수도 있습니다.

 

현재 사용하는 코드에서는, 가공은 필요 없지만
단순히 큰 객체를 가져오기에 재생성을 방지하는 목적으로 사용하였습니다.

 

사용방법

 

1. 평소처럼 slice 를 만들어줍니다.

<store / dummyAssets-slice.ts>

    import { createSlice, createSelector } from "@reduxjs/toolkit";
    import { RootState } from ".";
    import { DummyAssets } from "../components/helper/dummyAssets";
    import { DummyAttackAssets } from "../components/helper/dummyAssets";
    import { DummyDefenseAssets } from "../components/helper/dummyAssets";
    import { DummyCanaryAssets } from "../components/helper/dummyAssets";

    export interface TypeAlgorithms {
      itemId: string;
      itemName: string;
    }

    const initialState: {
      assets: TypeAlgorithms[];
      attackAssets: TypeAlgorithms[];
      defenseAssets: TypeAlgorithms[];
      canaryAssets: TypeAlgorithms[];
    } = {
      assets: DummyAssets,
      attackAssets: DummyAttackAssets,
      defenseAssets: DummyDefenseAssets,
      canaryAssets: DummyCanaryAssets,
    };

    const dummyAssetsSlice = createSlice({
      name: "select",
      initialState,
      reducers: {},
    });

    export default dummyAssetsSlice.reducer;

 

2. 평소처럼 store 의 index 안에서 store의 reducer 에 등록합니다.

<store / index.ts>

    import { configureStore } from "@reduxjs/toolkit";
    import userStrategySlice from "./user-strategy-detail-slice";
    import selectStrategySlice from "./select-strategy-algorithm-slice";
    import dummyAlgorithmSlice from "./dummyAlgorithm-slice";
    import dummyAssetsSlice from "./dummyAssets-slice";

    export const store = configureStore({
      reducer: {
        strategy: selectStrategySlice,
        userStrategy: userStrategySlice,
        dummyAlgorithm: dummyAlgorithmSlice,
        dummyAssets: dummyAssetsSlice,
      },
    });

    export type RootState = ReturnType<typeof store.getState>;
    export type AppDispatch = typeof store.dispatch;

 

3. store 에 등록했다면, 이제 createSelector 를 만들어서, useSelector 를 사용하기 위한 사전작업에 들어갑니다.

import { createSlice, createSelector } from "@reduxjs/toolkit";
import { RootState } from ".";
import { DummyAssets } from "../components/helper/dummyAssets";
import { DummyAttackAssets } from "../components/helper/dummyAssets";
import { DummyDefenseAssets } from "../components/helper/dummyAssets";
import { DummyCanaryAssets } from "../components/helper/dummyAssets";

...

const initialState: {
	...
};

const dummyAssetsSlice = createSlice({
	...
});

export default dummyAssetsSlice.reducer;

// 여기부터
export const dummyAssetsSelector = (state: RootState): TypeAlgorithms[] =>
  state.dummyAssets.assets;			// 필요한 값을 useSelector 처럼 미리 정의합니다.
export const dummyAttackAssetsSelector = (state: RootState): TypeAlgorithms[] =>
  state.dummyAssets.attackAssets;		// 필요한 값을 useSelector 처럼 미리 정의합니다.
export const dummyDefenseAssetsSelector = (
  state: RootState
): TypeAlgorithms[] => state.dummyAssets.defenseAssets;	// 필요한 값을 useSelector 처럼 미리 정의합니다.
export const dummyCanaryAssetsSelector = (state: RootState): TypeAlgorithms[] =>
  state.dummyAssets.canaryAssets;		// 필요한 값을 useSelector 처럼 미리 정의합니다.

export const allAssets = createSelector(	// createSelector 를 이용해, useSelctor 를 사용했을때, 어떤 값이 반환될지 정합니다.
  dummyAssetsSelector,
  dummyAttackAssetsSelector,		// 미리 정의한 값들을 넣어주고
  dummyDefenseAssetsSelector,
  dummyCanaryAssetsSelector,
  (assets, attackAssets, defenseAssets, canaryAssets) => ({
    assets,
    attackAssets,	// 이곳에서, 미리 정의한 값들을 사용하면 어떤 값을 반환할지 정합니다.
    defenseAssets,	// (...) 가 아니라 { return ...} 을 사용해도 무방합니다.
    canaryAssets,	// 현재 단순 값을 넣어준 이유는, 따로 이 값들을 처리해야할 함수가 없기 때문입니다.
  })
);


만약 처리후 사용하고 싶다면

(assets, attackAssets, defenseAssets, canaryAssets) => { return asset.filter(...) } 라던가

(assets, attackAssets, defenseAssets, canaryAssets) => { 
    if(...){
    ...
    return ...} 
    }
    
라던가 하여 사용할 수도 있습니다.

 

4. 이제 컴포넌트에서 useSelector를 이용해, 사전 정의한 selector 를 사용할 수 있습니다.

< components >

    import { useSelector } from "react-redux";
    import { allAssets } from "../../../../store/dummyAssets-slice";	// createSelector 로 만든

    const StaticStrategy = () => {
      const assets = useSelector(allAssets);

      console.log(assets.assets);
      console.log(assets.attackAssets);
      console.log(assets.defenseAssets);
      console.log(assets.canaryAssets);
      
      	...
        
=========================================================

미리 createSelector 로 정의한 값을 넣어주면 됩니다.
반환값은 설정한 대로

export const allAssets = createSelector(
		...
  (assets, attackAssets, defenseAssets, canaryAssets) => ({
    assets,attackAssets,defenseAssets,canaryAssets,})
);

assets,attackAssets,defenseAssets,canaryAssets 이 담긴 객체가 반환되었음을 알 수 있습니다.

 

 

댓글