首页 > 解决方案 > React useSelector hook 是传播对象还是只指定需要的字段更好

问题描述

如果我有一个巨大的对象(40 多个字段)并且我在组件中调用 useSelector 但只需要此类对象的 3 个字段,那么只调用 3 个字段会更好:

const user = useSelector((state) => ({
    id: state.userReducer.id,
    name: state.userReducer.name,
    lastName: state.userReducer.lastName
  }));

在这种情况下还可以,因为我们只需要 3 个字段,但如果我们需要 10+ 个字段,事情就会变得非常冗长。在这种情况下,扩展运算符可以为我节省大量代码,但这是一种不好的做法吗?由于代码混淆,性能或其他原因?

const user = useSelector((state) => ({ ...state.userReducer }));

标签: reactjsreact-reduxreact-hooks

解决方案


出于性能原因,您应该这样做:

const user = useSelector(state => state.userReducer);

您的选择器将在分派操作时运行。如果它返回不同的东西,那么在最后一次运行时,它将强制组件重新渲染。通过传播到一个新对象,它总是会返回一个新对象。因此,您的组件将在每个被调度的操作上重新渲染。

react-redux 文档

useSelector()还将订阅 Redux 存储,并在分派操作时运行您的选择器。

[...]

  • 当一个动作被调度时,useSelector()会对之前的选择器结果值和当前的结果值做一个参考比较。如果它们不同,组件将被强制重新渲染

  • [...]

  • useSelector()默认情况下使用严格===的引用相等检查,而不是浅相等

我也觉得钥匙userReducer有点尴尬。它只是user不是用户的减速器。reducer 是一个纯函数,它将动作转换为更改而不是数据对象。

编辑

正如@Adam 在评论中所指出的,如果您想进一步减少重新渲染的数量,您可以使用记忆选择器仅选择您感兴趣的状态。创建此类选择器的一个很好的库是reselect

// example

import { createSelector } from 'reselect'; 

const basicUserDataSelector = createSelector(
    ({user}) => user.id,
    ({user}) => user.name,
    ({user}) => user.lastName,
    (id, name, lastName) => ({id, name, lastName})
);

// usage

const user = useSelector(basicUserDataSelector);

在您的情况下,仅选择所需状态的最简单方法是告诉useSelector进行浅相等比较而不是参考相等比较。useSelector接受比较器函数作为第二个参数:

import { shallowEqual, useSelector } from 'react-redux'

const user = useSelector(
    ({user}) => ({
        id: user.id
        name: user.name
        lastName: user.lastName
    }),
    shallowEqual
);

最佳实践是仅从存储中提取所需的尽可能少的数据以限制重新渲染的数量。选择更多实际需要的数据的额外成本取决于所选数据的组件和更改频率。


推荐阅读