了解高阶组件之前,需要先了解下高阶函数,因为二者非常相似。
高阶函数:接受函数为参数或者返回值为函数。js中常见的filter、map、reduce等都是高阶函数,也包括memo函数。
高阶组件:高阶组件本身并不是一个组件,而是一个函数,它可以接受一个组件为参数并返回一个新的组件。高阶组件并不是react API的一部分,它是基于react的组合特性而形成的设计模式。 高阶组件在一些react的第三方库中非常常见:比如redux中的connect、react-router中的withRouter。 我们将组件作为参数传入进去后,也就相当于对这个组件做了一个劫持,那么是不是可以考虑做一些事情了。但是高阶组件在hooks出现后,用的越来越少了。
基础应用
import React, { PureComponent } from 'react'
class App extends PureComponent {
render() {
return (
<div>
App: {this.props.name}
</div>
)
}
}
//结合类组件的使用
//首先传入一个组件作为参数
function enhanceComponent(WrappedComponent) {
//最后又返回一个组件
class NewComponent extends PureComponent {
render() {
return <WrappedComponent {...this.props}/>
}
}
//也可以对所要返回的组件的名称做一个重命名
NewComponent.displayName = "Kobe";
return NewComponent;
}
//结合函数组件的使用
function enhanceComponent2(WrappedComponent) {
function NewComponent(props) {
return <WrappedComponent {...props}/>
}
NewComponent.displayName = "Kobe";
return NewComponent;
}
//使用高阶组件对导出的组件做一个包裹之后再进行导出
const EnhanceComponent = enhanceComponent2(App);
export default EnhanceComponent;
具体的应用场景:
1,增强props方式一:
为了体现出高阶组件的优势所在我们在这里先使用context共享数据的方式。
import React, { PureComponent, createContext } from 'react';
// 创建Context对象并赋予默认值
const UserContext = createContext({
nickname: "默认",
level: -1,
区域: "中国"
});
class Home extends PureComponent {
render() {
return (
<UserContext.Consumer>
{
user => {
return <h2>Home: {`昵称: ${user.nickname} 等级: ${user.level} 区域: ${user.region}`}</h2>
}
}
</UserContext.Consumer>
)
}
}
class About extends PureComponent {
render() {
return (
<UserContext.Consumer>
{
user => {
return <h2>About: {`昵称: ${user.nickname} 等级: ${user.level} 区域: ${user.region}`}</h2>
}
}
</UserContext.Consumer>
)
}
}
class App extends PureComponent {
render() {
return (
<div>
App
<UserContext.Provider value={{nickname: "why", level: 90, region: "中国"}}>
<Home/>
<About/>
</UserContext.Provider>
</div>
)
}
}
export default App;
看了上面的代码后,我们接下来使用高阶组件:
import React, { PureComponent } from 'react';
// 定义一个高阶组件
function enhanceRegionProps(WrappedComponent) {
return props => {
//在这里我们可以直接将region这个数据共享而不需要再一个个去单独进行传递了
return <WrappedComponent {...props} region="中国"/>
}
}
class Home extends PureComponent {
render() {
return <h2>Home: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
const EnhanceHome = enhanceRegionProps(Home);
const EnhanceAbout = enhanceRegionProps(About);
class App extends PureComponent {
render() {
return (
<div>
App
<EnhanceHome nickname="coderwhy" level={90}/>
<EnhanceAbout nickname="kobe" level={99}/>
</div>
)
}
}
export default App;
最后,我们也可以将二者结合起来使用。
import React, { PureComponent, createContext } from 'react';
// 定义一个高阶组件
function withUser(WrappedComponent) {
//在这里直接将共享的逻辑进行一个抽取,而不需要再在每一个组件中都去进行共享了
return props => {
return (
<UserContext.Consumer>
{
user => {
return <WrappedComponent {...props} {...user}/>
}
}
</UserContext.Consumer>
)
}
}
// 创建Context
const UserContext = createContext({
nickname: "默认",
level: -1,
区域: "中国"
});
class Home extends PureComponent {
render() {
return <h2>Home: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About: {`昵称: ${this.props.nickname} 等级: ${this.props.level} 区域: ${this.props.region}`}</h2>
}
}
class Detail extends PureComponent {
render() {
return (
<ul>
<li>{this.props.nickname}</li>
<li>{this.props.level}</li>
<li>{this.props.region}</li>
</ul>
)
}
}
const UserHome = withUser(Home);
const UserAbout = withUser(About);
const UserDetail = withUser(Detail);
class App extends PureComponent {
render() {
return (
<div>
App
<UserContext.Provider value={{nickname: "why", level: 90, region: "中国"}}>
<UserHome/>
<UserAbout/>
<UserDetail/>
</UserContext.Provider>
</div>
)
}
}
export default App;
2,登录鉴权
我们知道在浏览一些页面时是需要登录的,那么为了实现这个需求,我们不可能一个一个去给页面组件添加登录鉴权操作的;这个时候我们就可以利用高阶组件了,我们把鉴权操作写进高阶组件里面,这样在export需要鉴权操作的相关页面组件时,直接使用我们的高阶组件就可以了。
import React, { PureComponent } from 'react';
class LoginPage extends PureComponent {
render() {
return <h2>LoginPage</h2>
}
}
function withAuth(WrappedComponent) {
const NewCpn = props => {
const {isLogin} = props;
if (isLogin) {
return <WrappedComponent {...props}/>
} else {
return <LoginPage/>
}
}
NewCpn.displayName = "AuthCpn"
return NewCpn;
}
// 购物车组件
class CartPage extends PureComponent {
render() {
return <h2>CartPage</h2>
}
}
const AuthCartPage = withAuth(CartPage);
export default class App extends PureComponent {
render() {
return (
<div>
<AuthCartPage isLogin={true}/>
</div>
)
}
}
3,劫持生命周期函数
未使用高阶组件
import React, { PureComponent } from 'react';
class Home extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`Home渲染时间: ${interval}`)
}
render() {
return <h2>Home</h2>
}
}
class About extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`About渲染时间: ${interval}`)
}
render() {
return <h2>About</h2>
}
}
export default class App extends PureComponent {
render() {
return (
<div>
<Home />
<About />
</div>
)
}
}
使用高阶组件(也是对获取组件渲染时间的操作做一个公共提取)
import React, { PureComponent } from 'react';
function withRenderTime(WrappedComponent) {
return class extends PureComponent {
// 即将渲染获取一个时间 beginTime
UNSAFE_componentWillMount() {
this.beginTime = Date.now();
}
// 渲染完成再获取一个时间 endTime
componentDidMount() {
this.endTime = Date.now();
const interval = this.endTime - this.beginTime;
console.log(`${WrappedComponent.name}渲染时间: ${interval}`)
}
render() {
return <WrappedComponent {...this.props}/>
}
}
}
class Home extends PureComponent {
render() {
return <h2>Home</h2>
}
}
class About extends PureComponent {
render() {
return <h2>About</h2>
}
}
const TimeHome = withRenderTime(Home);
const TimeAbout = withRenderTime(About);
export default class App extends PureComponent {
render() {
return (
<div>
<TimeHome />
<TimeAbout />
</div>
)
}
}