React 学习笔记二 - HOC 高阶组件理解

官方定义

高阶组件(HOC)是 React 中用于<span style="color:red">复用组件逻辑</span>的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

HOC 是单词 Heigher Order Component 缩写

具体而言,<span style="color:red">高阶组件是参数为组件,返回值为新组件的函数。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

从官方定义中,我们可以抽取以下重点:

  • 高阶组件就是一个函数.
  • 此函数接受一个组件作为参数.
  • 此函数返回一个组件.
  • <span style="color:red"> 逻辑复用.或者说组件的功能扩展.

高阶组价,接受一个构造函数,返回一个构造函数. 所以,高阶组件就是一个概念. 它本质上也是高阶函数.

react-redux 里面的 connect 函数,就是一个高阶组件.

高级组件就是一个函数,接受一个一个组件(函数),返回一个新组件(函数)

理解高阶组件.

既然高阶组件,本质上是一个函数.那么我就给它一个组件作为入参,在返回一个组件即可.

import React, { useState } from 'react'
import './App.css';
/**
 * 
 * 官方定义高阶组件(HOC)是 React 中用于**复用组件逻辑**的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
   HOC 是单词 Heigher Order Component 缩写
   具体而言,高阶组件是参数为组件,返回值为新组件的函数。
 * 
 * 
 */

function HelloComponent(props) {
  return (
    <div>
      <h2>我是一个定义的函数式组件 <span style={{ fontSize: '20px', color: '#f40' }}>{props.title}</span> </h2>
    </div>
  )
}

// 高阶组件就是一个函数,接收一个组件,返回一个[新]的组件.
const withComponent = (Component) => {
  return function WrapperComponent() {
    const [title, setTitle] = useState('这是由内部组件 WrapperComponent 传递过来的 title')
    const changeTitle = () => {
      setTitle("这是新的 title")
    }
    return (
      <React.Fragment>
        <Component title={title} />
        <button onClick={changeTitle}>修改 title</button>
      </React.Fragment>
    )
  }
}

// 传入一个HelloComponent,作为入参
// 返回一个HocComponent作为新的组件
const HocComponent = withComponent(HelloComponent)

function App() {
  return (
    <div className="App">
      <h2>Hello HOC - Highter Order Component</h2>
      <HocComponent />
    </div>
  );
}
export default App;

可以正常工作.

![高阶组件简单使用images.jianshu.io/upload_images/2701794-d79d78664a4895aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


将逻辑复用(功能扩展)逻辑设定在高阶组件中.

上述例子,仅演示了高阶组件的用法和原理.

这次,我们把逻辑复用和功能扩展加上去.

这么一个例子

  • 有两组件. FooBar.
  • FooBar 组件的功能就是在屏幕视口宽度发生改变是,动态的把宽度数值显示出来.

import React from 'react'
import './App.css';
/**
 * 
 * 官方定义高阶组件(HOC)是 React 中用于**复用组件逻辑**的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
   HOC 是单词 Heigher Order Component 缩写
   具体而言,高阶组件是参数为组件,返回值为新组件的函数。
 * 
 * 
 */

class Foo extends React.PureComponent {
  state = {
    sw: document.documentElement.clientWidth
  }

  componentDidMount () {
    window.addEventListener('resize', () => {
      this.setState(() => ({ sw: document.documentElement.clientWidth }))
    }, false)
  }

  componentWillUnmount () {
    window.removeEventListener('resize')
  }

  render () {
    return (
      <p className='FooContent'>当前的屏幕宽度为:{this.state.sw}</p>
    )
  }
}

class Bar extends React.PureComponent {
  state = {
    sw: document.documentElement.clientWidth
  }

  componentDidMount () {
    window.addEventListener('resize', () => {
      this.setState(() => ({ sw: document.documentElement.clientWidth }))
    }, false)
  }

  componentWillUnmount () {
    window.removeEventListener('resize')
  }

  render () {
    return (
      <button className='btn-content'>当前屏幕宽度为:{this.state.sw}</button>
    )
  }
}

function App () {
  return (
    <div className="App">
      <h2>Hello HOC - Highter Order Component</h2>
      <Foo />
      <Bar />
    </div>
  );
}
export default App;

效果如图:

[图片上传失败...(image-c468a2-1642682879317)]

功能确实实现了.但是两组件除了渲染的位置,其他地方都是一致的.

Vue中,我们可以使用 mixins 来解决组件间逻辑重复定义的问题.

state , componentDidMount, componentWillUnmount 逻辑重复.

React 中,我们就可以使用 Highter Order Component 高阶组件的方式,来抽取组件重复定义的逻辑部分.

// 定义一个高阶组件函数,来抽取逻辑重复的位置,提高逻辑复用.
const withResize = (Component) => {
    //xxxxx
}

具体代码如下:

import React from 'react'
import './App.css';
/**
 * 
 * 官方定义高阶组件(HOC)是 React 中用于**复用组件逻辑**的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
   HOC 是单词 Heigher Order Component 缩写
   具体而言,高阶组件是参数为组件,返回值为新组件的函数。
 * 
 * 
 */

class Foo extends React.PureComponent {
  // state = {
  //   sw: document.documentElement.clientWidth
  // }

  // componentDidMount () {
  //   window.addEventListener('resize', () => {
  //     this.setState(() => ({ sw: document.documentElement.clientWidth }))
  //   }, false)
  // }

  // componentWillUnmount () {
  //   window.removeEventListener('resize')
  // }

  render () {
    return (
      <p className='FooContent'>当前的屏幕宽度为:{this.props.sw}</p>
    )
  }
}

class Bar extends React.PureComponent {
  // state = {
  //   sw: document.documentElement.clientWidth
  // }

  // componentDidMount () {
  //   window.addEventListener('resize', () => {
  //     this.setState(() => ({ sw: document.documentElement.clientWidth }))
  //   }, false)
  // }

  // componentWillUnmount () {
  //   window.removeEventListener('resize')
  // }

  render () {
    return (
      <button className='btn-content'>当前屏幕宽度为:{this.props.sw}</button>
    )
  }
}

// 定义一个高阶组件函数,来抽取逻辑重复的位置,提高逻辑复用.
const withResize = (Component) => {
  return class WrappedComponent extends React.PureComponent {
    //#region 抽离组件通用的逻辑部分
    state = {
      sw: document.documentElement.clientWidth
    }

    componentDidMount () {
      window.addEventListener('resize', () => {
        this.setState(() => ({ sw: document.documentElement.clientWidth }))
      }, false)
    }

    componentWillUnmount () {
      window.removeEventListener('resize')
    }
    //#endregion

    render () {
      // 将需要的数据以 props 的方式传递给被包装的组件
      return <Component {...this.state} />
    }
  }
}

const FooWithResize = withResize(Foo)
const BarWithResize = withResize(Bar)

function App () {
  return (
    <div className="App">
      <h2>Hello HOC - Highter Order Component</h2>
      <h3>将通用逻辑抽离,以便复用!</h3>
      {/* <Foo />
      <Bar /> */}
      <FooWithResize />
      <BarWithResize />
    </div>
  );
}
export default App;


  • 将通用逻辑从 FooBar 组件中抽离.起到逻辑复用的作用.
  • FooBar 需要的数据以 props 的形式传入即可.
高阶组件抽取通用逻辑以便复用.gif

效果和上述是一致的.


总结

  • 高阶组件就是一个函数仅此而已(javascript中不就是函数和对象吗?)
  • 高阶组件接受一个组件(可以是函数组件,也可以是class 组件)作为参数(普通函数,构造函数).
  • 返回一个新组件(可以是函数组件,也可是 class 组件),返回一个普通函数或者是构造函数.
  • 所以高阶组件也是一个高阶函数.
  • 将通用的逻辑抽离在高阶组件中,已达到复用的目的.

码云地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容