最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does this code use both useMemo and createSelector? - Stack Overflow

programmeradmin0浏览0评论

The React-Redux documentation provides this example for when a selector is used in multiple ponent instances and depends on the ponent'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?

The React-Redux documentation provides this example for when a selector is used in multiple ponent instances and depends on the ponent'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?

Share Improve this question asked Jun 18, 2020 at 13:14 Jack DamonJack Damon 3411 gold badge5 silver badges15 bronze badges 2
  • 2 It's a parameterized selector. In the redux doc example they don't pass anything to the selector creator function but you can curry the parameter(s) (see my documentation). When you give each ponent it's own selector then memoization will work. – HMR Commented Jun 18, 2020 at 13:24
  • Ahh - your 'Parameterized and Memoized' section perfectly helps me understand it. Thank you! – Jack Damon Commented Jun 18, 2020 at 14:12
Add a ment  | 

2 Answers 2

Reset to default 5

Because each ponent needs its own unique instance of the selector for correct memoization behavior. If many ponents 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 ponent, each selector can pass in its own separate args and get consistent memoization.

Slightly unrelated to the question, but I think in this case, we can use useCallback to have the same result w/o having an extra layer of function in makeNumOfTodosWithIsDoneSelector.

import React, { useCallback } 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 = useCallback(
    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} />
    </>
  )
}

发布评论

评论列表(0)

  1. 暂无评论