이번에는 createSelector를 사용해보도록 하겠습니다.
react hooks를 배울 때 useMemo를 기억을 떠올려보도록하겠습니다.
useMemo는 하나의 상태값을 변경하였을 때 해당 컴포넌트가 다시 랜더링이 될때 불필요한 작업들을 하는경우가 발생합니다.
즉 똑같은 데이터를 다시 받아와서 똑같은 결과를 뿌릴는거를 다시 할필요 없게 만들어줍니다.
useMemo 예제는 [React] 9. react-hooks(useMemo, useCallback, useContext) 를 보시면 되겠습니다.
createSelector도 인자가 변경되지 않으면 재계산을 수행하지 않습니다.
createSelector를 사용하면 함수 인자가 메모된 함수에 캐시되기 때문에 함수의 인자가 이전 호출 때의 값과 다를 경우에만 셀렉터가 다시 계산을 수행합니다.
뭐 굳이 createSelector를 사용하지않고 useMemo를 사용해도 됩니다만, createSelector에서 만든 새로운 state값을 여러 곳에서 사용을 한다면
useMemo를 사용할 때 계산해야하는 함수들을 만들고, 그 함수에다가 useMeno를 작성해줘야하는 불편함이 있지만, createSelector는 state값으로 새로 만들어주는 거기 때문에 코드량이 많이 줄어드는 장점을 가지고 있고, 전체 state값을 가져올 필요 없이 특정 state값만 가지고 올수도 있습니다.
그럼 이전 게시글에서 만들었던 Redux-Saga에 createSlice를 적용한 예제를 가지고 해보겠습니다.
sliceReducer에 createSelector를 임포트해줍니다.
import { createAction, createReducer, createSlice, createSelector } from "@reduxjs/toolkit";
그리고 createSelector를 사용하여 만들어보겠습니다.
const selectAllState = createSelector( state => state.number, state => state.error, state => state.errDesc, (number, error, errDesc) => { console.log("selectAllState"); return {number, error, errDesc}; } );
현재 소스를 보시면 state에서 number, error, errDesc값을 return해서 (number, error, errDesc)=> {
}
로 return 값을 받아줍니다.
그리고 3개의 return 된 값을 {}감싸서 다시 return을 해줍니다.
return한 {number, error, errDesc}를 useSelector()로 뽑아서 사용할 수있습니다.
createSelector를 담은 selectAllState변수를 호출해야 만들어 지겠죠?
export const sSelector = { all : state => selectAllState(state[name]) };
이렇게 sSelector를 밖에서 참조할 수 있게 export를 해주고,
key값인 all value값인 selectAllState(state[name])형식입니다.
state를 인자로 보내줬기때문에 createSelector에서 state값을 뽑을 수 있었던겁니다.
이렇게 createSelector로 만든 state를 useSelector로 뽑아서 사용해보겠습니다.
컨테이너 컴포넌트에서 sliceReducer를 임포트해줍니다.
import * as slice from '../modules/sliceReducer';
그리고 useSelector로 slice의 export한 sSelector를 참조하여 key값으로 value값을 가져오도록합니다.
const {number, error, errDesc} = useSelector(slice.sSelector.all); const testState = useSelector(state => state.CSCount);
useSelector로 createSelector로 state 가져오는 법과 기존에 사용하던 state가져오는법을 비교했을때 createSelector는 number, error, errDesc 값만 존재하지만,
기존 state값 가져오는 거에서는 number, error, errDesc, test까지 데이터를 가져오는 걸 확인하실 수 있습니다.
createSelector를 하나 더 만들어보겠습니다.
const selectTest = createSelector( state => state.test, (test) => { console.log("selectTest"); return test*2 } ); export const sSelector = { all : state => selectAllState(state[name]), test : state => selectTest(state[name]) };
이렇게 test값에 *2를 하여 return해주는 state값입니다.
그리고 나서 실행을 시켜보면 개발자 도구 창에
이렇게
selectAllState를 실행하여 console찍한 내용과,
selectTest를 실행하여 console에 찍힌 내용이 나옵니다.
그리고 몇초 후에 api통신으로 받은 결과로 sliceReducer에서 state를 변경해주는데 이때 state값의 test는 변경하지 않았습니다.
getNumberS : (state, {payload : param}) => { debugger; state.number = param[0]; state.error = false; state.errDesc = ""; }, getNumberF : (state, {payload : param}) => { debugger; state.number = 1; state.error = true; state.errDesc = param.error.message; }
이렇게 return 되면서 리랜더링 되는 과정에서 다시 selectAllState는 실행이되지만, selectTest는 실행이 되지 않습니다.
useMemo처럼 리랜더링 되기 전에 받은 test값이 리랜더링 되고 나서 받은 test값이 동일하기때문에 해당 함수를 타지 않고 넘어갑니다.
이러한 방식으로도 불필요한 랜더링을 줄여 최적화하는 방식이 있습니다.