gloriaJun / til

Lessoned Learned
3 stars 0 forks source link

[React] reselect #41

Open gloriaJun opened 5 years ago

gloriaJun commented 5 years ago

Reselect is a simple library for creating memoized, composable selector functions. Reselect selectors can be used to efficiently compute derived data from the Redux store.

서버로부터 전달받은 데이터의 가공이 필요한 경우에 일반적으로 아래와 같은 방법을 사용할 수 있다.

  1. reducer에서 가공하여 store에 저장
  2. component의 render 내부에서

위와 같이 사용하는 경우 다음과 같은 단점이 있다고 한다

  1. reducer에서 가공하여 store에 저장
    • 모든 목록을 미리 저장하므로 메모리를 많이 사용하게 된다.
    • 같은 데이터가 여러 곳에 중복으로 저장될 수 있고, 이러한 문제는 데이터 동기화 작업이 복잡하게 되어 일부 목록만 업데이트되는 버그가 생길 수도 있다.
  2. component의 render 내부에서
    • 매 render가 호출 될 때마다 연산이 발생하여 렌더링 성능 저하가 발생한다.

이러한 문제점을 보완할 수 있는 방법으로는 reselect를 이용할 수 있다.

예를 들어서 아래의 코드와 같이 mapStateToProps 내부에서 데이터 가공을 하는 경우와 createSelector를 이용하여 가공을 하는 경우에

const getSelectAccountId = createSelector(
  state => state.Account.accounts,
  (state, props) => props.account,
  (accounts, account) => {
    console.log('createSelector', accounts, account);
    const { accountId } = !ObjectUtils.isEmpty(accounts)
      ? ObjectUtils.isEmpty(account)
        ? AccountUtils.getMainAccount(accounts)
        : account
      : {};
    return accountId;
  }
);

const mapStateToProps = (state, props) => {
  const accounts = state.Account.accounts;
  const { account } = props;
  const defaultAccount = !ObjectUtils.isEmpty(accounts)
    ? ObjectUtils.isEmpty(account)
      ? AccountUtils.getMainAccount(accounts)
      : account
    : {};
  console.log('mapStateToProps', accounts, props.account);

  return {
    accounts,
    account: defaultAccount,
    selectAccountId: getSelectAccountId(state, props),
  };
};

콘솔로그를 확인해보면, mapStateToProps 내부의 경우에는 action이 발생하여 state가 변경되는 시점마다 연산이 발생하지만.. (빨간 네모 친 부분이 불필요한 연산이 발생한 부분이라고 판단되는 부분임) createSelector 내부에 정의된 부분은 selector로 전달된 state가 변경되는 시점에만 연산이 발생하는 것을 확인할 수 있다.

스크린샷 2019-07-31 오후 4 53 28

useMemo

추가로 Reac Hooks를 이용하는 경우, useMemoreselect와 비슷한 성능 개선 효과가 있는 것 같다.

  useMemo(() => {
    console.log('useMemo', accounts, account);
  }, [accounts, account]);

  useEffect(() => {
    console.log('useEffect', accounts, account);
  }, [accounts, account]);

  useEffect(() => {
    console.log('useEffect-accounts', accounts, account);
  }, [accounts]);
  useEffect(() => {
    console.log('useEffect-account', accounts, account);
  }, [account]);

위와 같이 컴포넌트 내부에 정의한 뒤에 콘솔 로그를 확인해보면 useMemo의 경우, 원하는 시점에 수행되는 것을 확인할 수 있었다.

스크린샷 2019-07-31 오후 11 47 30

References

gloriaJun commented 5 years ago

hooks로만 이용해서 구현한다면 reselect는 필요없을 수도.... 하지만, 그렇지 않다면 reselect를 이용하면 불필요한 연산을 줄임으로써 성능을 개선시킬 수 있다.