HOC(高阶组件)

/*HOC(高阶组件): 接收一个组件,返回包装后的组件(增强组件)- 不是React API- 是一种设计模式,类似于装饰器模式- ≈ Mixin && > Minxinconst 包装后的组件 = 高阶组件(被包装的组件);// e.g. const Wrapper = withRouter(NavBar);高阶组件会把所有接收到的props,传递给被包装的组件(透传)ref 和 key 类似,不是一个prop,所以不会透传,ref会绑定到外层的包装容器上 | 解决方法可以参考下面的 <<处理ref>>
* */

怎样包装组件?

/*怎样包装组件?第一种: 普通包装export时就包装import React from 'react';import Hoc from './Hoc';class Header extends React.Component {render() {return <span>{ this.props.count }</span>}};export default Hoc(Header);==========import后再包装:import Header from './header';import Hoc from './Hoc';const EnhanceHeader = Hoc(Header);const Home = () => {return (<div><EnhanceHeader count={1} /></div>)}第二种: 装饰器包装,只能在类组件中使用import React from 'react';import Hoc from './Hoc';@Hocexport default class Header extends React.Component {render() {return <span>{ this.props.count }</span>}};=======@Hocclass Header extends React.Component {render() {return <span>{ this.props.count }</span>}};export default Header;
* */

定义一个简单的HOC

/*定义一个简单的HOC,接收一个组件,返回一个组件import React from 'react';// 返回类组件export default function Hoc(WrappedComponent) {/*return class extends React.Component {}- 在 React Developer Tools 中展示的名字是 Componentreturn class Wrapper extends React.Component {}- 在 React Developer Tools 中展示的名字是 Wrapper*\return class extends React.Component {render() {return <WrappedComponent {...this.props} />;}};}// 返回函数式组件export default function Hoc(WrappedComponent) {/*return function(props) {}- 在 React Developer Tools 中展示的名字是 Anonymousreturn function Wrapper(props) {}- 在 React Developer Tools 中展示的名字是 Wrapper*\return function Wrapper(props) {return <WrappedComponent {...props} />;};}
* */

给Hoc传参

/*给Hoc传参// Hoc,可以接受任意参数export default function Hoc(WrappedComponent, title, user, data) {return class Wrapper extends React.Component {render() {return <WrappedComponent {...this.props} />}};};// 包装时传参const EnhanceHeader = Hoc(Header, 'title', { name: '霖'}, [1, 2, 3]);
* */

Hoc嵌套

/*Hoc嵌套,函数柯里化的原理// Hoc1: 给组件添加title属性export default function Hoc1(WrappedComponent, title) {return class extends React.Component {render() {return <WrappedComponent title={title} {...this.props} />}};};// Hoc2: 修改组件的显示内容export default function Hoc2(WrappedComponent, content) {return class extends WrappedComponent { // 这里用了反向继承render() {const elementTree = super.render(); // React用Js对象来模拟Dom树结构,可以通过修改Js对象的属性来操纵数据console.log(elementTree); // 不太了解里面的结构可以打印出来 + 官网cloneElement() 了解一下const newElementTree = React.cloneElement(elementTree, { children: `你的内容已被劫持: ${content}` });return newElementTree;}};};// 被包裹的组件export default class Header extends React.Component {render() {const { title } = this.props;return (<span title={title}>默认内容</span>)}};// 使用import Hoc1 from './Hoc1';import Hoc2 from './Hoc2';/*包装过程1. const Wrapper = Hoc2(Header, '内容');2. Hoc1(Wrapper)**const EnhanceHeader = Hoc1(Hoc2(Header, '内容'), '标题');export default function Home() {return (<div><EnhanceHeader /></div>);};* */

处理ref

/*处理refe.g. Hoc1(Hoc2(Content))<Content ref={myRef} /> 给Content绑定的ref会绑定到Hoc1上,且不会继续向下传递第一种方法 React.forwardRef ===============在 Hoc1外面 用React.forwardRef()对ref做处理,用props来传递ref0. 在高阶组件外面包裹forwardRef,拦截获取ref,增加一个props(xxx={ref}),真实组件通过props.xxx获取1. 使用时传 ref={XXXX}  // 和第二种方法不同的地方2. 用forwardRef的第二个参数获取 ref3. 增加一个新的props,用来向下转发ref  e.g. forwardedRef={ref}4. 真实组件中绑定 ref={props.forwardedRef}const Home = (props) => {const connectRef = useRef(null);return (<div><Content ref={connectRef} /></div>);};// 被包装组件const Content = (props) => {return (<div><input type="password" ref={props.forwardedRef} /></div>);};// forwardRef的第二个入参可以接收ref,在Hoc外层对ref做处理export default React.forwardRef((props, ref) => {const Wrapper = React.memo(Content);  // Hoc// forwardRef包裹的是Wrapper// 需要在Wrapper中把ref向下传递给真实组件// Wrapper中增加一个props属性,把ref对象作为props传给子组件return <Wrapper {...props} forwardedRef={ref} />;});第二种方法 ==========0. 使用时就用一个props来保存ref1. 使用时传 xxx={ref}  // 和第一种方法的不同点2. 真实组件中绑定 ref={props.xxx}const Home = (props) => {const connectRef = useRef(null);return (<div><Content forwardedRef={connectRef} /></div>);};// 定义高阶组件export const Hoc = (WrappedComponent) => {class Wrapper extends React.Component {render() {return <WrappedComponent {...props} />}}}// 被包装的组件const Content = (props) => {return (<div><input type="password" ref={props.forwardedRef} /></div>);};// 包装过程export default Hoc(Content);* */

使用被包装组件的静态方法

/*使用被包装组件的静态方法// 被包装组件,增加静态属性和方法export default class Header extends React.Component {static displayName = 'header';static showName = () => {console.log(this.displayName);};render() {return <span>header</span>}};// HOCexport default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {render() {return <WrappedComponent {...this.props} />}};};===========// Hoc包装后的组件拿不到静态方法import Header from './header';import Hoc from './Hoc';const EnhanceHeader = Hoc(Header);export default function Home() {console.log(EnhanceHeader.displayName);   // undefinedEnhanceHeader.showName();                 // undefinedreturn <EnhanceHeader />}=============// 解决方法1:拷贝静态方法到HOC上export default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {static displayName = WrappedComponent.displayName;  // 必须知道被包装组件中有什么静态方法static showName = WrappedComponent.showName;render() {return <WrappedComponent {...this.props} />}};};==============// 解决方法2:自动拷贝所有静态属性和方法import React from 'react';import hoistNonReactStatic from 'hoist-non-react-statics';export default function Hoc(WrappedComponent) {class Wrapper extends React.Component {render() {return <WrappedComponent {...this.props} />}};hoistNonReactStatic(Wrapper, WrappedComponent);return Wrapper;};==============// 解决方法3:导出组件时,额外导入静态属性和方法class Header extends React.Component {render() {return <span>header</span>}};const displayName = 'header';function showName() {console.log(Header.displayName);};Header.displayName =displayName;Header.showName = showName;export default Headerexport { displayName, showName }// 导入时import Header, { displayName, showName } from './header';import Hoc from './Hoc';const EnhanceHeader = Hoc(Header);export default function Home() {console.log(displayName);   // headershowName();                 // headerreturn <EnhanceHeader />}
* */

拦截传给被包装组件的props,对props进行增删改

/*拦截传给被包装组件的props,对props进行增删改export default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {render() {// 过滤一些仅在当前Hoc中使用的props,不进行不必要的透传const { forMeProps, forOtherProps } = this.props;// 在该HOC内部定义,需要注入到被包装组件的额外的属性或方法const injectProps = some-state-or-method;         // 通常是state或实例方法// 为被包装组件传递上层的props + 额外的propsreturn (<WrappedComponentinjectProps={injectProps}    // 传递需要注入的额外props{...forOtherProps}           // 透传与后续相关的props/>)}}}e.g.Hoc接收一个额外的props 'dealUpper',如果为true,将data转换成大写dealUpper只在该Hoc中使用,所以没必要传给被包装的组件// HOCexport default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {render() {const { dealUpper, ...forOtherProps } = this.props;const { data } = forOtherProps;if (dealUpper) {Object.assign(forOtherProps, {data: data.toUpperCase()})}return <WrappedComponent {...forOtherProps} />}};};// 导出Hoc包装后的增强组件import React from 'react';import Hoc from './Hoc1';class Header extends React.Component {render() {console.log(this.props); // { data: 'ABC' }return <span>{this.props.data}</span>}};export default Hoc(Header); // 导出包装后的增强组件// 导入使用import Header from './header';const Home = () => {return <Header data={'abc'} dealUpper />}
* */

用HOC提取一些复杂的公共逻辑,在不同组件中扩展不同的功能

/*用HOC提取一些复杂的公共逻辑,在不同组件中扩展不同的功能import React from 'react';export const Hoc = (WrappedComponent, namespace) => {class Wrapper extends React.Component {state = {data: []}// 抽离的相同请求方法componentDidMount = () => {const { dispatch } = this.props;dispatch({type: `${namespace}/queryData`, // 动态请求不同的storepayload: {},callback: res => {if (res) {this.setState({data: res.data})}}})}render() {return <WrappedComponent { ...this.props } data={this.state.data} />}}}// 包装A组件import Hoc from './Hoc';const A = ({ data }) => {... 省略请求数据的逻辑return (data.map(item => item));}export default MyHoc(A, 'a');// 包装B组件import Hoc from './Hoc';const B = ({ data }) => {... 省略请求数据的逻辑return (<ul>{data.map((item, index) => {return <li key={index}><{item}/li>}}</ul>)}export default Hoc(B, 'b');
* */

让不受控组件变成受控组件

/*让不受控组件变成受控组件// Hoc组件export default function Hoc(WrappedComponent) {return class Wrapper extends React.Component {state = {value: ''};onChange = (e) => {this.setState({value: e.target.value})};render() {const newProps = {value: this.state.value,onChange: this.onChange};return <WrappedComponent {...this.props} {...newProps} />}};};// 普通组件class InputComponent extends React.Component {render() {return <input {...this.props} />}}// 包装export default Hoc(InputComponent);
* */

反向继承

/*反向继承(在Hoc中使用被包装组件内部的状态和方法)- 反向继承的组件要是类组件,函数组件不行export const Hoc = (WrappedComponent) => {class Wrapper extends WrappedComponent { // super ≈ WrappedComponent里面的thisrender() {if (!this.props.data) {return <span>loading....</span>} else {return super.render() // 调用被包装组件的render()方法}}}}====export default function Hoc(WrappedComponent) {return class extends WrappedComponent {render() {const elementTree = super.render(); // React用Js对象来模拟Dom树结构,可以通过修改Js对象的属性来操纵数据console.log(elementTree); // 不太了解里面的结构可以打印出来 + 官网cloneElement() 了解一下const newElementTree = React.cloneElement(elementTree, { children: `你的内容已被劫持` });return newElementTree;}};};
* */

渲染劫持

/*渲染劫持e.g. 控制组件是否渲染(可以做全局的loading效果,没有数据时显示loading...)// 基本的实现export const LoadingHoc = (WrappedComponent) => {class Wrapper extends React.Component {render() {if (!this.props.data) {return <span>loading....</span>} else {return <WrappedComponent {...this.props} />}}}}// 用反向继承实现export const LoadingHoc = (WrappedComponent) => {class Wrapper extends WrappedComponent { // super ≈ WrappedComponent里面的thisrender() {if (!this.props.data) {return <span>loading....</span>} else {return super.render() // 调用被包装组件的render()方法}}}}======e.g. 劫持渲染的内容export default function Hoc2(WrappedComponent) {return class extends WrappedComponent { // 这里用了反向继承render() {const elementTree = super.render(); // React用Js对象来模拟Dom树结构,可以通过修改Js对象的属性来操纵数据console.log(elementTree); // 不太了解里面的结构可以打印出来 + 官网cloneElement() 了解一下const newElementTree = React.cloneElement(elementTree, { children: `你的内容已被劫持` });return newElementTree;}};};
* */

配置包装名

/*配置包装名:在调试工具 React Developer Tools 中更容易被找到e.g. 高阶组件为Hoc,被包装组件为WrappedComponent, 显示的名字应该是 Hoc(WrappedComponent)// 返回类组件export default function Hoc(WrappedComponent) {return class extends React.Component {/*没有在Hoc中定义 static displayName = 'XXX';- React Developer Tools 中展示的名字是 Anonymous没有在被包装组件中定义 static displayName = 'XXX';- React Developer Tools 中展示的名字是 undefined Hoc在被包装组件中定义 static displayName = 'header';- React Developer Tools 中展示的名字是 header Hoc*\static displayName = `Hoc(${WrappedComponent.displayName});render() {return <WrappedComponent {...this.props} />;}};}// 返回函数式组件export default function Hoc(WrappedComponent) {/*return function(props) {}- 在 React Developer Tools 中展示的名字是 Anonymousreturn function Wrapper(props) {}- 在 React Developer Tools 中展示的名字是 Wrapper*return function Wrapper(props) {return <WrappedComponent {...props} />;};}=======export default function Hoc(WrappedComponent) {const Wrapper = (props) => {return <WrappedComponent {...props} />;};/*没有在被包装组件中定义 static displayName = 'XXX';- React Developer Tools 中展示的名字是 undefined Hoc在被包装组件中定义 static displayName = 'header';- React Developer Tools 中展示的名字是 header Hoc*\Wrapper.displayName = `Hoc(${WrappedComponent.displayName})`;return Wrapper;}=====// 被包裹组件export default class Header extends React.Component {static displayName = 'header';render() {return <span>{ this.props.count }</span>}};* */

不要在render中使用HOC

/*不要在render中使用HOCe.g.export default class Home extends React.Component {render() {// 每次render都会创建一个新的Wrapper// Wrapper1 !== Wrapper2// 导致高阶组件会卸载和重新挂载,状态会丢失(e.g. checkbox的选中丢失 | state被清空)× const Wrapper = Hoc(WrappedComponent);return <Wrapper />}}=========√ const Wrapper = myHoc(WrappedComponent);export default class Home extends React.Component {render() {return <Wrapper />}}
* */

Hoc的渲染顺序

/*Hoc的渲染顺序Hoc(Header)componentDidMount: Header -> HOCcomponentWillUnMount: HOC -> Header
* */

HOC 和 Mixin

/*HOC 和 MixinHOC- 属于函数式编程思想- 被包裹组件感知不到高阶组件的存在- 高阶组件返回的组件会在原来的基础上的到增强Mixin- 混入模式,会在被包装组件上不断增加新的属性和方法- 被包裹组件可感知- 需要做处理(命名冲突、状态维护)
* */

React 高阶组件HOC使用总结相关推荐

  1. [react] 高阶组件(HOC)有哪些优点和缺点?

    [react] 高阶组件(HOC)有哪些优点和缺点? HOC 优点 通过传递props去影响内层组件的状态,不直接改变内层组件的状态,降低了耦合度 缺点 组件多层嵌套, 增加复杂度与理解成本 ref隔 ...

  2. 「react进阶」一文吃透React高阶组件(HOC)

    一 前言 React高阶组件(HOC),对于很多react开发者来说并不陌生,它是灵活使用react组件的一种技巧,高阶组件本身不是组件,它是一个参数为组件,返回值也是一个组件的函数.高阶作用用于强化 ...

  3. react 高阶组件hoc使用

    react 高阶组件hoc使用 1. 为什么使用高阶组件 2. 具体使用 2.1原代码: 2.2 使用hoc封装后 1. 为什么使用高阶组件 高阶组件(HOC)是 React 中用于复用组件逻辑的一种 ...

  4. react 高阶组件HOC实现组件复用

    目录 一 使用步骤 二 显示鼠标坐标 三 鼠标随图片移动 四 设置displayName 五 传递props 高阶组件HOC: Higher-Order Component,是一个函数,接收要包装的组 ...

  5. React高阶组件(HOC)的写法归纳

    react高阶组件的写法总结 什么是高阶组件 高阶组件要解决什么问题 高阶组件的写法 什么是高阶组件 何为高阶组件(HOC),根据官方文档的解释:"高阶组件是react中复用组件逻辑的一项高 ...

  6. React 高阶组件HOC详解

    参考链接: https://juejin.cn/post/6844903815762673671 https://juejin.cn/post/6844904050236850184 前言 高阶组件与 ...

  7. 条件渲染之react高阶组件——HOC

    定义:它接受任何输入(多数时候是一个组件)并返回一个组件作为输出.返回的组件是输入组件的增强版本,并且可以在 JSX 中使用.不是react api而是一种设计模式. 作用:强化组件,复用逻辑,提升渲 ...

  8. React 高阶组件HOC、设置displayName、高阶组件传递props

    文章目录 1. 高阶组件 使用步骤 2. 高阶组件设置displayName 3.传递props 1. 高阶组件 目的:实现状态逻辑复用.实现功能增强.返回一个增强组件. 高阶组件(HOC,Highe ...

  9. React高阶组件(HOC)模型理论与实践

    什么是HOC? HOC(全称Higher-order component)是一种React的进阶使用方法,主要还是为了便于组件的复用.HOC就是一个方法,获取一个组件,返回一个更高级的组件. 什么时候 ...

最新文章

  1. 一分钟带你了解什么是“复杂度” 算法上的O(1)、O(n)、O(logn) 这些都是什么❓❓
  2. 烽火传递(dp+单调队列)
  3. Linux查看版本当前操作系统内核信息
  4. 每个Java开发人员都应该阅读的10本书
  5. C++11 function用法 可调用对象模板类
  6. 单元测试的思考与实践
  7. java模拟安卓get请求,Java模拟HTTP Get Post请求实现论坛自动回帖功能
  8. 产品读书《缔造企鹅:产品经理是这样炼成的》
  9. linux 安装 navicat
  10. wow.js动画插件
  11. itest(爱测试) 4.2.1 发布,开源BUG 跟踪管理 敏捷测试管理软件
  12. 计算机网络三层交换机配置,综述三层交换机配置实例 附详细命令解释
  13. arp -s命令报错,ARP项添加失败:请求的操作需要提升。
  14. NVIDIA_CUDA版本查看方法_WIN10
  15. 计算一个球的体积的程序HTML,【编程1小时】球体表面积和体积计算
  16. 星起航:抖音小店如何对接达人?
  17. 我在ADAU1452这颗音频DSP上,用MFXLMS算法来做Active Noise Cancellation(ANC)
  18. rockchip rk3368(px5)车载开发之路2,屏幕正常显示(不对的地方是UI)
  19. 若依ruoyiAOP切面用于数据过滤和权限处理实例
  20. AD9361的Rx增益控制(一)增益控制的阈值检测器

热门文章

  1. 昨天的《实话实说》周尚元造飞机
  2. 阿里云服务器永久性修改主机名方法
  3. Rasa课程、Rasa培训、Rasa面试、Rasa实战系列之Typo Robustness
  4. 让Fedora 19支持ThinkPad鼠标中键和小红点实现滚轮效果
  5. python计算召回率_分词结果准确率、召回率计算-python
  6. 大学考试的LINUX试题, 全做对了,保证你面试没问题(转)
  7. Excel中最精确的计算年龄的公式
  8. html传奇广告,吸引人气传奇三种广告投放方法
  9. 【转载】设置端口映射或DMZ主机---将内网web服务器映射入公网
  10. oracle数据库频繁被锁,oracle数据库用户频繁被锁