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);
从官方定义中,我们可以抽取以下重点:
- 高阶组件就是一个函数.(没有说是构造函数,在React中,构造函数,你继承自 React.Component,它就是一个组件了)
- 此函数接受一个组件作为参数.(就是接受一个继承自React.Component的构造函数作为参数)
- 此函数返回一个组件.(必须返回一个继承自React.Component的构造函数)
- <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)
将逻辑复用(功能扩展)逻辑设定在高阶组件中.
上述例子,仅演示了高阶组件的用法和原理.
这次,我们把逻辑复用和功能扩展加上去.
这么一个例子
- 有两组件.
Foo
和Bar
. -
Foo
和Bar
组件的功能就是在屏幕视口宽度发生改变是,动态的把宽度数值显示出来.
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;
- 将通用逻辑从
Foo
和Bar
组件中抽离.起到逻辑复用的作用. - 将
Foo
和Bar
需要的数据以props
的形式传入即可.
效果和上述是一致的.
总结
- 高阶组件就是一个函数仅此而已(
javascript
中不就是函数和对象吗?) - 高阶组件接受一个组件(可以是函数组件,也可以是class 组件)作为参数(普通函数,构造函数).
- 返回一个新组件(可以是函数组件,也可是 class 组件),返回一个普通函数或者是构造函数.
- 所以高阶组件也是一个高阶函数.
- 将通用的逻辑抽离在高阶组件中,已达到复用的目的.