on
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로 확인해보면 다음과 같이 나온다.
자바스크립트 객체 구조와 똑같이 때문에 할당을 통해서 데이터를 가져올 수 있다.
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} />
);
};