Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this code use both useMemo and createSelector?

The React-Redux documentation provides this example for when a selector is used in multiple component instances and depends on the component's props.

import React, { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'

const makeNumOfTodosWithIsDoneSelector = () =>
  createSelector(
    state => state.todos,
    (_, isDone) => isDone,
    (todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
  )

export const TodoCounterForIsDoneValue = ({ isDone }) => {
  const selectNumOfTodosWithIsDone = useMemo(
    makeNumOfTodosWithIsDoneSelector,
    []
  )

  const numOfTodosWithIsDoneValue = useSelector(state =>
    selectNumOfTodosWithIsDone(state, isDone)
  )

  return <div>{numOfTodosWithIsDoneValue}</div>
}

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <TodoCounterForIsDoneValue isDone={true} />
      <span>Number of unfinished todos:</span>
      <TodoCounterForIsDoneValue isDone={false} />
    </>
  )
}

In the function TodoCounterForIsDoneValue, why does the author wrap makeNumOfTodosWithIsDoneSelector with useMemo? My understanding of createSelector from reselect is that it generates a memoized selector, so what is the purpose of "double" memoizing this selector?

like image 677
Jack Damon Avatar asked Jun 18 '20 13:06

Jack Damon


1 Answers

Because each component needs its own unique instance of the selector for correct memoization behavior. If many components use the same selector instance, and each pass in their own different arguments (such as selectThingById(state, props.itemId)), the selector will never memoize right. By creating a unique instance per component, each selector can pass in its own separate args and get consistent memoization.

like image 108
markerikson Avatar answered Oct 18 '22 06:10

markerikson