英文:
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
中的所有内容(与 Redux
、React-Redux
和 Redux 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';
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论