Redux Toolkit: 我应该同时使用 useSelector 和 useCallback 吗?

huangapple go评论65阅读模式
英文:

Redux-toolkit: should I use both useSelector and useCallback

问题

I was previously using useSelector from Redux using something like:

  const todo = useSelector((state) => state.todos[props.id])

as documented. However, in another related library called proxy-memorize, they recommend instead to use (for their own library):

useSelector(useCallback(memoize(state => ({  }))))

but now I’m confused: should I use useCallback inside useSelector generally when I’m not using proxy-memorize? If not, why does proxy-memorize require it?

英文:

I was previously using useSelector from Redux using something like:

  const todo = useSelector((state) => state.todos[props.id])

as documented. However, in another related library called proxy-memorize, they recommend instead to use (for their own library):

useSelector(useCallback(memoize(state => ({ … }))))

but now I’m confused: should I use useCallback inside useSelector generally when I’m not using proxy-memorize? If not, why does proxy-memorize requires it?

答案1

得分: 1

> generally when I’m not using proxy-memorize?

通常情况下,当我不使用 proxy-memorize 时,不需要在 useSelector 中使用 useCallback 包裹选择器函数。React-redux 和 useSelector 钩子在更新期间会进行一些相等性检查

> When the function component renders, the provided selector function
> will be called and its result will be returned from the useSelector()
> hook. (A cached result may be returned by the hook without re-running
> the selector if it's the same function reference as on a previous
> render of the component.
)

当函数组件渲染时,提供的选择器函数将被调用,并且其结果将从 useSelector() 钩子返回。如果选择器的函数引用与组件以前的渲染中的相同函数引用相同,钩子可能会返回一个缓存的结果。

> If not, why does proxy-memorize requires it?

如果不这样做,为什么 proxy-memorize 需要它呢?

Note also from the docs that

还请注意文档中提到:

> With useSelector(), returning a new object every time will always
> force a re-render by default.

使用 useSelector(),每次返回一个新对象都会默认强制重新渲染。

Where one of the proposed solutions is

其中一种建议的解决方案是:

> Use Reselect or a similar library to create a memoized selector that
> returns multiple values in one object, but only returns a new object
> when one of the values has changed.

使用 Reselect 或类似的库创建一个记忆化的选择器,该选择器将多个值返回到一个对象中,但仅在其中一个值发生更改时返回一个新对象。

Using useSelector(state => ({ … })) would trigger a force rerender even if the returned object was equivalent. The memoize function from proxy-memoize serves to memoize the returned result, and wrapping it in the useCallback hook serves to memoize, e.g. provide, a stable function reference to the useSelector hook. useSelector(useCallback(memoize(state => ({ … })))) provides the broadest form of stability optimization by memoizing (A) the selector callback function, and (B) memoizing the selector function results. Note in the usage with react redux section this is the suggested pattern to use if you are not using reselect.

使用 useSelector(state => ({ … })) 会触发强制重新渲染,即使返回的对象是等效的。proxy-memoize 中的 memoize 函数用于记忆返回的结果,并将其包装在 useCallback 钩子中,用于记忆,例如,提供稳定的函数引用useSelector 钩子。useSelector(useCallback(memoize(state => ({ … })))) 提供了最广泛的稳定性优化形式,通过记忆 (A) 选择器回调函数,和 (B) 选择器函数的结果。请注意,在 使用 React Redux 部分中,如果您不使用 reselect,这是建议的使用模式。

I've not ever heard of proxy-memoize, but a Reselect version of the code may look something like the following:

我以前从未听说过 proxy-memoize,但代码的 Reselect 版本可能如下所示:

import { createSelector } from 'reselect';

const selectTodos = state => state.todos;

export const selectTodoById = createSelector(
  [selectTodos, (state, id) => id],
  (todos, id) => todos[id],
);
const todo = useSelector(state => selectTodoById(props.id))

selectTodoById 选择器函数是一个记忆化的选择器,例如,通过使用 state.todos 和传递的 id 值作为输入来记忆化所选的 todo 对象。只有当这两个值中的任何一个发生更改时,选择器函数才会重新计算其值,否则它只会返回记忆化的值。

* 注意: 如果您使用的是 Redux Toolkit,它会重新导出 Reselect 中的所有内容(与 ReduxReact-ReduxRedux Toolkit 的维护者相同,他们还维护着 Reselect):

import { createSelector } from '@reduxjs/toolkit';
英文:

> useSelector(useCallback(memoize(state => ({ … }))))
>
> but now I’m confused: should I use useCallback inside useSelector
> generally when I’m not using proxy-memorize?

Generally, no, you do not need to memoize the selector function via the useCallback hook, useSelector((state) => state.todos[props.id]) is fine. React-redux and the useSelector hook applies some equality checks during updates. Italicized and bolded text emphasis is mine to highlight what I believe to be relevant with regards to selector function reference stability.

> When the function component renders, the provided selector function
> will be called and its result will be returned from the useSelector()
> hook. (A cached result may be returned by the hook without re-running
> the selector if it's the same function reference as on a previous
> render of the component.
)
>
> 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. The default comparison is a
> strict === reference comparison.

> If not, why does proxy-memorize requires it?

Note also from the docs that

> With useSelector(), returning a new object every time will always
> force a re-render by default.

Where one of the proposed solutions is

> Use Reselect or a similar library to create a memoized selector that
> returns multiple values in one object, but only returns a new object
> when one of the values has changed.

Using useSelector(state => ({ … })) would trigger a force rerender even if the returned object was equivalent. The memoize function from proxy-memoize serves to memoize the returned result, and wrapping it in the useCallback hook serves to memoize, e.g. provide, a stable function reference to the useSelector hook. useSelector(useCallback(memoize(state => ({ … })))) provides the broadest form of stability optimization by memoizing (A) the selector callback function, and (B) memoizing the selector function results. Note in the usage with react redux section this is the suggested pattern to use if you are not using reselect.

I've not ever heard of proxy-memoize, but a Reselect version of the code may look something like the following:

import { createSelector } from 'reselect'; // *

const selectTodos = state => state.todos;

export const selectTodoById = createSelector(
  [todos, (state, id) => id],
  (todos, id) => todos[id],
);
const todo = useSelector((state) => selectTodoById(props.id))

The selectTodoById selector function is a memoized selector, e.g. memoizes the selected todo object using the state.todos and passed id value as inputs. Only when either of these two values changes the selector function recompute its value, otherwise it simply returns the memoized value.

* Note: If you are using Redux-Toolkit, it re-exports all of Reselect (the same maintainers of Redux, React-Redux, and Redux-Toolkit also maintain Reselect):

import { createSelector } from '@reduxjs/toolkit';

huangapple
  • 本文由 发表于 2023年6月6日 07:27:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76410573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定