useSelector销毁与多个调用

时间:2019-11-27 14:18:47

标签: reactjs react-redux react-hooks

最近我一直在阅读react-redux文档https://react-redux.js.org/next/api/hooks 还有一个与“平等比较和更新”相关的部分,内容为:“多次调用useSelector(),每次调用返回一个字段值”。

第一种方法:

const { open, importId, importProgress } = useSelector((importApp) => importApp.productsImport);

第二种方法:

const open = useSelector((importApp) => importApp.productsImport.open);
const importId = useSelector((importApp) => importApp.productsImport.importId );
const importProgress = useSelector((importApp) => importApp.productsImport.importProgress);

那么有什么真正的区别吗?还是由于销毁useSelector钩会在检查引用时遇到麻烦?

5 个答案:

答案 0 :(得分:7)

将操作分派到Redux存储时,useSelector()仅执行严格的“ ===”引用比较。这不会进行浅层检查。

因此,如果要从存储中检索多个值,则必须多次调用useSelector(),每次调用都从state返回单个字段值。或在shallowEqual中使用react-redux

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

const data = useSelector(state => state.something, shallowEqual)

有关详细说明,请参见https://react-redux.js.org/next/api/hooks#equality-comparisons-and-updates

答案 1 :(得分:5)

只是奠定基础:在调度动作之后,将调用您传递给useSelector()的选择器。如果它返回的值与上次分派动作时返回的值不同,则组件将重新呈现。

破坏确实是错误的方法,但是这里的首要答案是完全不相关的。这些文档所引用的场景是选择器每次都创建一个新对象,就像您在mapStateToProps()函数中所做的那样。这将导致组件在每次分派操作时都会重新渲染,无论该操作执行了什么操作,因为从技术上讲,即使实际数据没有更改,选择器返回的值在内存上也是一个不同的对象。 在这种情况下,您需要担心严格相等和浅层相等比较。但是,您的选择器并非每次都创建一个新对象。如果已分派的动作没有修改importApp.productsImport,则它将是与内存中完全相同的对象,从而呈现了所有模拟结果。

相反,这里的问题是,当您实际上只在乎状态的某些特定属性时,您正在选择整个状态。考虑到importApp.productsImport除了openimportIdimportProgress以外可能还具有其他属性。如果这些其他属性发生更改,则即使您的组件未对其进行引用,也将不必要地对其进行重新渲染。原因很简单:选择器返回importApp.productsImport,并且该对象已更改。 Redux无法知道openimportIdimportProgress是您真正关心的唯一属性,因为您没有选择这些属性;您选择了整个对象

解决方案

因此,要选择多个属性而不进行不必要的重新渲染,您有两个选择:

  • 使用多个useSelector()钩子,每个钩子在商店中选择一个属性。
  • 具有一个useSelector()钩子和一个选择器,该选择器将商店中的多个属性组合到一个对象中。您可以通过以下方式做到这一点:
    • 使用来自reselect的记忆选择器。
    • 只需编写一个函数即可根据state的特定属性创建一个新对象并返回它。如果这样做,您将然后担心严格的相等性和浅层的相等性比较。

为此,我觉得实际上是多个useSelector()挂钩。文档着重提到了这一点

每次调用useSelector()都会创建一个Redux存储的单独订阅。

但是,我认为,纯粹出于这个原因,与单个调用相比,多个调用是否会真正导致实际性能下降,还有待观察,在我看来,除非您有一个建议,否则担心此问题可能是过度优化的拥有数百或数千个订阅的大型应用程序。如果您使用单个useSelector()钩子,那么到那时您基本上只是在编写一个mapStateToProps函数,我觉得这打败了很多使用钩子开始的魅力,尤其是因此,如果您正在编写TypeScript。而且,如果您然后想要分解结果,那将使其更加麻烦。我还认为,使用多个钩子绝对更符合Hooks API的总体精神。

答案 2 :(得分:2)

如何选择多个状态 ::

const [village, timeZone, date] = useSelector((state) => [
    state.active_village,
    state.time_zone,
    state.date,
  ]);

答案 3 :(得分:1)

这个答案没有很多技术细节。请查看其他答案以更好地理解。在这里,我只提到用例场景。希望对您有所帮助:

假设:productsImport 只有您提到的字段(open、importId、importProgress

1. 假设您要使用 useSelector 获取单个组件中的所有字段,那么我会说您应该使用第一种方法。因为使用第二种方法会增加几行额外的代码而没有其他好处。无论如何,只要任何字段的值更新,您的组件就会重新渲染。

2. 现在,假设您正在使用和更新一个组件 (Component1) 中的 importId 以及另一个组件 (Component2) 中的其余字段。而且,这些组件也应该在同一个窗口/屏幕上呈现。那么你应该使用第二种方法

> options(digits=16)
> 1/3
[1] 0.3333333333333333
> options(digits=2)
> 1/3
[1] 0.33
> 

如果您在这里使用第一方法,那么您的两个组件将在您每次更新任何字段时重新渲染。

答案 4 :(得分:0)

从性能的角度来看,第一种方法更好。 在引擎盖下,破坏性代码被转换为

var temp = useSelector((importApp) => importApp.productsImport);
var open = temp.open;
var importId = temp.importId;
var importProgress = temp.importProgress;

在第二种方法中,商店被访问3次,这是更昂贵的操作。