useSelector 최적화

Hyun·2021년 10월 5일
1

리액트 리덕스

목록 보기
10/14

리액트 컴포넌트에서 리덕스 상태를 조회해서 사용할 때, 최적화를 하기 위해서 어떤 상황을 고려해야 하는지 알아보자.

지난번 할 일 목록을 만들 때에는 프리젠테이셔널 컴포넌트에 React.memo 를 사용하여 리렌더링 최적화를 해줬다. 컨테이너 컴포넌트에서는 어떤 것들을 검토해야 하는지 알아보자.

크롬의 react devtools 를 설치하고 카운터의 +, - 를 눌렀을 때, 하단의 할 일 목록이 리렌더링되진 않지만 할 일 목록의 항목을 토글할 때에는 카운터가 리렌더링 되는 것을 확인할 수 있다.

기본적으로 useSelector 를 사용해서 리덕스 스토어의 상태를 조회할 땐, 만약 상태가 바뀌지 않았으면 리렌더링을 하지 않는다.

TodosContainer 의 경우 카운터 값이 바뀔 때 상태를 나타내는 todos 값에는 변화가 없기 때문에, 리렌더링 되지 않는 것이다.

const todos = useSelector(state => state.todos);

반면 CounterContainer 의 경우 할 일 목록의 토글을 클릭했을 때를 살펴보자.

const { number, diff } = useSelector(state => ({
  number: state.counter.number,
  diff: state.counter.diff
}));

CounterContainer 에서는 useSelector hook 을 사용해 상태를 조회하긴 하지만 그 상태를 바로 변수에 주는게 아니라, 사실상 상태를 조회에 새로운 객체 { number, diff } 를 만들어 주기 때문에, 상태가 바뀌었는지 바뀌지 않았는지를 확인할 수가 없어 위 화면과 같은 낭비 렌더링이 이루어지고 있다.

이를 최적화 하기 위해선 두가지 방법이 있다.

첫번째: useSelector 를 여러번 이용하는 것이다.

const number = useSelector(state => state.counter.number);
const diff = useSelector(state => state.counter.diff);

이렇게 하면 상태를 조회하고, 상태가 바뀌지 않았으면 number, diff 값도 변화가 없기 때문에 리렌더링을 하지 않는다. 해당 값들 중 하나라도 바뀌었을 때만 컴포넌트가 리렌더링 된다.

두번째: react-redux 의 shallowEqual 함수를 useSelector 의 두번째 인자로 전달해 주는 것이다.

import React from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease, setDiff } from '../modules/counter';

function CounterContainer() {
  const { number, diff } = useSelector(state => ({
    number: state.counter.number,
    diff: state.counter.diff
  }),
  	shallowEqual
);

  (...)

useSelector 의 두번째 파라미터는 equalityFn 이다.

equalityFn?: (left: any, right: any) => boolean

이전 값과 다음 값을 비교하여 true(같음) 가 나오면 리렌더링을 하지 않고 false(다름) 가 나오면 리렌더링한다.

shallowEqaul 은 react-redux 에 내장되어있는 함수로서, 객체안의 가장 겉에 있는 값들을 모두 비교해준다.

여기서 말하는 겉에 있는 값이란, 만약 다음과 같은 객체가 있다면

const object = {
  a: {
    x: 3,
    y: 2,
    z: 1
  },
  b: 1,
  c: [{ id: 1 }]
}

가장 겉에 있는 값은 object.a, object.b, object.c 이다. shallowEqaul 에서는 해당 값들만 비교하고 object.a.x 또는 object.c[0] 과 같은 값들은 비교하지 않는다.

위 상황에서는 number 와 diff 가 겉에 있는 값에 해당된다.

세번째: 객체 비구조화를 이용하여 상태를 조회하고 바로 변수에 줄 수 있다.

const { number, diff } = useSelector((state) => state.counter);

상당히 깔끔한 방법인 것 같다.

profile
better than yesterday

0개의 댓글