Firebase - Channel을 Redux Store에서 가져오기(5)

컴포넌트에서 Redux store에 접근하기

react-redux는 컴포넌트에서 리덕스 스토어에서 값을 가져올 수 있는 두 가지 방법을 제공한다. connect() 함수를 이용하는 방법이 있고 hook을 이용하는 방법이 있다. hook을 이용하는 방법이 더욱 직관적이기 때문에 사용하기 더욱 쉽다고 생각한다.

useSelector hook

store에서 상태를 가져오는 훅은 useSelector(selector)이다. useSelector는 인자로 함수(selector)를 받게 되는데 useSelector는 이 함수에게 redux의 단일 글로벌 state를 인자로 넘기게 된다.

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

channel을 redux아래에 두었기 때문에 현재 redux의 state는 다음과 같은 구조로 이루어져 있다.


{
    channel: {
        currentChanel: {
            // channel data
        }
    }
}

Redux-devtools로 확인해보면 다음과 같이 나온다.

channel4

자바스크립트 객체 구조와 똑같이 때문에 할당을 통해서 데이터를 가져올 수 있다.

const { currentChanel } = useSelector((state) => state.channel);

// or

const currentChannel = useSelector((state) => state.channel.currentChannel);

useSelector and re-render

useSelector()를 잘 사용하기 위해서는 useSelector()는 언제 실행하고 언제 렌더를 유발하는지 알아야 한다. 컴포넌트가 렌더될 때 selector 함수가 실행된다. selector 함수는 state를 반환한다. 만약 state가 이전 결과와 같다면 selector는 실행되지 않고 캐시된 이전 결과를 반환한다.

액션이 디스패치되고 이전 state와 달라졌을 때, useSelector()는 re-render를 유발한다.

However, when an action is dispatched to the Redux store, useSelector() only forces a re-render if the selector result appears to be different than the last result.

useSelector 는 기본적으로 === 비교 (strict comparison)을 통해서 이전 결과와 비교를 수행한다.

만약 selector가 새로운 객체를 반환한다면 항상 re-render를 유발한다.

// 다음 selector 함수는 항상 새로운 객체를 반환한다.
// 따라서 useSelector함수가 실행될 때마다 re-render가 일어난다.
const { currentChannel, currentUser } = useSelector((state) => ({
  currentChannel: state.channel.currentChannel,
  currentUser: state.user.currentUser,
}));

// object comparison
const a = { a: 1 };
const b = { a: 1 };

console.log(a === b); // false

이렇게 re-render가 발생하면 불필요한 렌더링이 일어날 수 있다.

최적화

불필요한 렌더링을 줄이기 위해 최적화를 할 필요가 있다. 일단 selector가 새로운 객체를 반환하도록 하지 않도록 하고 여러 useSelector를 이용한다.

const currentChannel = useSelector((state) => state.channel.currentChannel);
const currentUser = useSelector((state) => state.user.currentUser);

react-redux 에서 제공하는 shallowEqual을 두번째 인자로 넣어준다.

const currentChannel = useSelector(
  (state) => state.channel.currentChannel,
  shallowEqual
);

객체를 비교하는 경우, 직접 equality를 비교하는 함수를 만들어 인자로 넣을 수 있다.

const checkEquality = () => {
  // check equality
};

const currentChannel = useSelector(
  (state) => state.channel.currentChannel,
  checkEquality
);

코드 작성하기

현재 채널에 대한 정보가 필요한 컴포넌트에서 useSelector()를 호출하면 된다.

// ChannelsContainer.jsx

const MessageContainer = () => {
  const currentChannel = useSelector((state) => state.channel.currentChannel);
  // other
  return (
    // JSX
    <Message currentChannel={currentChannel} />
  );
};