首页 > 其他分享 >React组件设计模式-纯组件,函数组件,高阶组件

React组件设计模式-纯组件,函数组件,高阶组件

时间:2022-11-08 12:44:08浏览次数:85  
标签:HOC React connect key props 组件 设计模式

一、组件

(1) 函数组件

如果你想写的组件只包含一个 render 方法,并且不包含 state,那么使用函数组件就会更简单。我们不需要定义一个继承于 React.Component 的类,我们可以定义一个函数,这个函数接收 props 作为参数,然后返回需要渲染的元素。

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
    {props.value}    </button>
  );
}

(2) React.Component

shouldComponentUpdate 仅检查了 props.color 或 state.count 是否改变。如果这些值没有改变,那么这个组件不会更新

class CounterButton extends React.Component {

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }
}

(3) PureComponent

如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 props 和 state 中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 React.PureComponent 就行了。

class CounterButton extends 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

Button
Input

以下组件适合PureComponent

Radio
Checkbox
Option

二、高阶函数

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):

function withSubscription(WrappedComponent) {
  class WithSubscription extends React.Component {/* ... */}
  WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
  return WithSubscription;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

(4) 注意事项:

<1> 不要在 render 方法中使用 HOC

render() {
  // 每次调用 render 函数都会创建一个新的 EnhancedComponent
  // EnhancedComponent1 !== EnhancedComponent2

  const EnhancedComponent = enhance(MyComponent);

  // 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!

  return <EnhancedComponent />;
}

<2>务必复制静态方法

有时在 React 组件上定义静态方法很有用。例如,Relay 容器暴露了一个静态方法 getFragment 以方便组合 GraphQL 片段。

但是,当你将 HOC 应用于组件时,原始组件将使用容器组件进行包装。这意味着新组件没有原始组件的任何静态方法。

// 定义静态函数
WrappedComponent.staticMethod = function() {/*...*/}
// 现在使用 HOC
const EnhancedComponent = enhance(WrappedComponent);

// 增强组件没有 staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true
为了解决这个问题,你可以在返回之前把这些方法拷贝到容器组件上:

参考React实战视频讲解:进入学习

你可以使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法:

import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  hoistNonReactStatic(Enhance, WrappedComponent);
  return Enhance;
}

除了导出组件,另一个可行的方案是再额外导出这个静态方法。

// 使用这种方式代替...
MyComponent.someFunction = someFunction;
export default MyComponent;

// ...单独导出该方法...
export { someFunction };

// ...并在要使用的组件中,import 它们
import MyComponent, { someFunction } from './MyComponent.js';

<3> Refs 不会被传递

虽然高阶组件的约定是将所有 props 传递给被包装组件,但这对于 refs 并不适用。那是因为 ref 实际上并不是一个 prop - 就像 key 一样,它是由 React 专门处理的。如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。

这个问题的解决方案是通过使用 React.forwardRef API(React 16.3 中引入)

三、React Redux 的 connect

React Redux 的 connect 函数是一个 返回高阶组件的高阶函数

最常见的 HOC 签名如下:

// React Redux 的 `connect` 函数
const ConnectedComment = connect(commentSelector, commentActions)(CommentList);

刚刚发生了什么?!如果你把它分开,就会更容易看出发生了什么。

// connect 是一个函数,它的返回值为另外一个函数。
const enhance = connect(commentListSelector, commentListActions);

// 返回值为 HOC,它会返回已经连接 Redux store 的组件
const ConnectedComment = enhance(CommentList);

这种形式可能看起来令人困惑或不必要,但它有一个有用的属性。最大化可组合性
像 connect 函数返回的单参数 HOC 具有签名 Component => Component。 输出类型与输入类型相同的函数很容易组合在一起。

// 而不是这样...
const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))

// ... 你可以编写组合工具函数
// compose(f, g, h) 等同于 (...args) => f(g(h(...args)))

const enhance = compose(
  // 这些都是单参数的 HOC
  withRouter,
  connect(commentSelector)
)
const EnhancedComponent = enhance(WrappedComponent)

//(同样的属性也允许 connect 和其他 HOC 承担装饰器的角色)

四、其他

(1)key

每当一个列表重新渲染时,React 会根据每一项列表元素的 key 来检索上一次渲染时与每个 key 所匹配的列表项。如果 React 发现当前的列表有一个之前不存在的 key,那么就会创建出一个新的组件。如果 React 发现和之前对比少了一个 key,那么就会销毁之前对应的组件。如果一个组件的 key 发生了变化,这个组件会被销毁,然后使用新的 state 重新创建一份。

我们强烈推荐,每次只要你构建动态列表的时候,都要指定一个合适的 key。

如果你没有指定任何 key,React 会发出警告,并且会把数组的索引当作默认的 key。但是如果想要对列表进行重新排序、新增、删除操作时,把数组索引作为 key 是有问题的

显式地使用 key={i} 来指定 key 确实会消除警告,但是仍然和数组索引存在同样的问题,所以大多数情况下最好不要这么做。

组件的 key 值并不需要在全局都保证唯一,只需要在当前的同一级元素之前保证唯一即可。

标签:HOC,React,connect,key,props,组件,设计模式
From: https://www.cnblogs.com/xiaofeng123aa/p/16869295.html

相关文章

  • 基于 React 和 Redux 的 API 集成解决方案
    在前端开发的过程中,我们可能会花不少的时间去集成API、与API联调、或者解决API变动带来的问题。如果你也希望减轻这部分负担,提高团队的开发效率,那么这篇文章一定会对你......
  • 10个值得你去试试的React开发工具
    JavaScript每天都在出现大量的框架和工具,而React是除了上次我们提到的Vue和Ember之外另一款比较流行的框架。但因为新的工具每天都在不断的出现,开发者在尝试时总会有些不知......
  • 7种在React中使用CSS的方式
    第一种:在组件中直接使用style不需要组件从外部约会css文件,直接在组件中书写。importreact,{Component}from"react";constdiv1={width:"300px",margin:"30px......
  • Xmake v2.7.3 发布,包组件和 C++ 模块增量构建支持
    Xmake是一个基于Lua的轻量级跨平台构建工具。它非常的轻量,没有任何依赖,因为它内置了Lua运行时。它使用xmake.lua维护项目构建,相比makefile/CMakeLists.txt,配置语......
  • React的5种高级模式
    本文概述了5种现代高级React模式,包括集成代码、优点和缺点,以及在公共库中的具体用法。像每个React开发者一样,你可能已经问过自己以下问题之一我如何建立一个可重复使用......
  • vue内置组件component的使用
    <keep-alive><componenttype="type":is="componentName":isEdit="isEdit":ref="componentName"@allFormSave="allFormSave":infoData="i......
  • 设计模式—建造者模式
    我们通常在开发中见到一种链式的构造对象,比如:demoAnimatordemoAnimator=newdemoAnimator.Builder().setFade(true).setDuration(1000).build();那他是怎样实现的呢、......
  • 「前端游戏开发体验」我用react实现网页游戏的全过程(包括规则设计)
    关于游戏的灵感来源今年元宵节的时候,我玩的小游戏里面有限时任务,可以解锁节日限定物品,于是那几天我玩的很欢乐很积极。端午节到来之前,我想玩一下身份转换,从玩家转换到游戏策......
  • 第三章、react高级
    目录七、setState详细解析和React性能优化1、setState异步更新2、setState同步更新3、setState数据的合并4、setState本身的合并5、React更新机制6、列表中keys的作用7、re......
  • 封装大屏组件 screenfull
    错误场景:使用大屏插件screenFull报错:in./node_modules/screenfull/index.js Moduleparsefailed:Unexpectedtoken(59:42)Youmayneedanappropriateloaderto......