React 组件设计模式 - 纯组件,函数组件,高阶组件
一、组件
(1) 函数组件
如果你想写的组件只包含一个 render 方法,并且不包含 state,那么使用函数组件就会更简单。我们不需要定义一个继承于 React.Component 的类,我们可以定义一个函数,这个函数接收 props 作为参数,然后返回需要渲染的元素。
(2) React.Component
shouldComponentUpdate 仅检查了 props.color 或 state.count 是否改变。如果这些值没有改变,那么这个组件不会更新
(3) PureComponent
如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 props 和 state 中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 React.PureComponent 就行了。
大部分情况下,你可以使用 React.PureComponent 来代替手写 shouldComponentUpdate。但它只进行浅比较 (例如:1 == 1 或者 ture==true,数组和对象引用是否相同),所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能使用它了。
不要在 props 和 state 中改变对象和数组,如果你在你的父组件中改变对象,你的 PureComponent 将不会更新。虽然值已经被改变,但是子组件比较的是之前 props 的引用是否相同,所以不会检测到不同。
因此,你可以通过使用 es6 的 assign 方法或者数组的扩展运算符或者使用第三方库,强制返回一个新的对象。
当数据结构很复杂时,情况会变得麻烦,存在性能问题。(比较原始值和对象引用是低耗时操作。如果你有一列子对象并且其中一个子对象更新,对它们的 props 和 state 进行检查要比重新渲染每一个子节点要快的多。)
(4) 何时使用 Component 或 PureComponent ?
<1> 当组件是独立的,组件在页面中的个数为 1 或 2 的,组件有很多 props、state,并且当中还有些是数组和对象的,组件需要每次都渲染的,使用 Component
<2> 当组件经常作为子组件,作为列表,组件在页面中数量众多,组件 props, state 属性少,并且属性中基本没有数组和对象,组件不需要每次都渲染,只有变化了才渲染,使用 PureComponent
凭主观,我觉得以下组件适合 Component
以下组件适合 PureComponent
二、高阶函数
HOC ( 高阶组件 higherOrderComponent ) 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。(组件是 React 中代码复用的基本单元。)
高阶组件例如 Redux 的 connect 和 Relay 的 createFragmentContainer。
(1)HOC 不会修改传入的组件,也不会使用继承来复制其行为。
相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。
(2)HOC 应该透传与自身无关的 props
HOC 为组件添加特性。自身不应该大幅改变约定。HOC 应该透传与自身无关的 props,HOC 返回的组件与原组件应保持类似的接口。
(3)约定:包装显示名称以便轻松调试 HOC
创建的容器组件会与任何其他组件一样,会显示在 React Developer Tools 中。为了方便调试,请选择一个显示名称,以表明它是 HOC 的产物。
最常见的方式是用 HOC 包住被包装组件的显示名称。比如高阶组件名为 withSubscription,并且被包装组件的显示名称为 CommentList,显示名称应该为 WithSubscription(CommentList):
(4) 注意事项:
<1> 不要在 render 方法中使用 HOC
<2>务必复制静态方法
有时在 React 组件上定义静态方法很有用。例如,Relay 容器暴露了一个静态方法 getFragment 以方便组合 GraphQL 片段。
但是,当你将 HOC 应用于组件时,原始组件将使用容器组件进行包装。这意味着新组件没有原始组件的任何静态方法。
参考 React 实战视频讲解:进入学习
你可以使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法:
除了导出组件,另一个可行的方案是再额外导出这个静态方法。
<3> Refs 不会被传递
虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。
这个问题的解决方案是通过使用 React.forwardRef API(React 16.3 中引入)
三、React Redux 的 connect
React Redux 的 connect
函数是一个 返回高阶组件的高阶函数!
最常见的 HOC 签名如下:
刚刚发生了什么?!如果你把它分开,就会更容易看出发生了什么。
这种形式可能看起来令人困惑或不必要,但它有一个有用的属性。最大化可组合性。像 connect 函数返回的单参数 HOC 具有签名 Component => Component。 输出类型与输入类型相同的函数很容易组合在一起。
四、其他
(1)key
每当一个列表重新渲染时,React 会根据每一项列表元素的 key 来检索上一次渲染时与每个 key 所匹配的列表项。如果 React 发现当前的列表有一个之前不存在的 key,那么就会创建出一个新的组件。如果 React 发现和之前对比少了一个 key,那么就会销毁之前对应的组件。如果一个组件的 key 发生了变化,这个组件会被销毁,然后使用新的 state 重新创建一份。
我们强烈推荐,每次只要你构建动态列表的时候,都要指定一个合适的 key。
如果你没有指定任何 key,React 会发出警告,并且会把数组的索引当作默认的 key。但是如果想要对列表进行重新排序、新增、删除操作时,把数组索引作为 key 是有问题的。
显式地使用 key={i} 来指定 key 确实会消除警告,但是仍然和数组索引存在同样的问题,所以大多数情况下最好不要这么做。
组件的 key 值并不需要在全局都保证唯一,只需要在当前的同一级元素之前保证唯一即可。
评论