Before understanding higher-order components, you need to understand the lower higher-order functions, because they are very similar.
Higher order function: accept function as parameter or return value as function. The common filter, map and reduce functions in js are high-order functions, including memo functions.
High order component: the high-order component itself is not a component, but a function. It can accept a component as a parameter and return a new component. The high-order component is not a part of the react API. It is a design pattern based on the combination characteristics of react. High order components are very common in some third-party libraries of react, such as connect in redux and withRouter in react router. After we pass in the component as a parameter, it is equivalent to hijacking the component. Can we consider doing something. However, after the appearance of hooks, higher-order components are used less and less.
Basic application
import React, { PureComponent } from 'react' class App extends PureComponent { render() { return ( <div> App: {this.props.name} </div> ) } } //Use of combined class components //First pass in a component as a parameter function enhanceComponent(WrappedComponent) { //Finally, another component is returned class NewComponent extends PureComponent { render() { return <WrappedComponent {...this.props}/> } } //You can also rename the name of the component to be returned NewComponent.displayName = "Kobe"; return NewComponent; } //Combined with the use of function components function enhanceComponent2(WrappedComponent) { function NewComponent(props) { return <WrappedComponent {...props}/> } NewComponent.displayName = "Kobe"; return NewComponent; } //Use high-level components to package the exported components before exporting const EnhanceComponent = enhanceComponent2(App); export default EnhanceComponent;
Specific application scenarios:
1. Enhanced props mode 1:
In order to reflect the advantages of high-level components, we first use context to share data.
import React, { PureComponent, createContext } from 'react'; // Create a Context object and assign default values const UserContext = createContext({ nickname: "default", level: -1, region: "China" }); class Home extends PureComponent { render() { return ( <UserContext.Consumer> { user => { return <h2>Home: {`nickname: ${user.nickname} Grade: ${user.level} region: ${user.region}`}</h2> } } </UserContext.Consumer> ) } } class About extends PureComponent { render() { return ( <UserContext.Consumer> { user => { return <h2>About: {`nickname: ${user.nickname} Grade: ${user.level} region: ${user.region}`}</h2> } } </UserContext.Consumer> ) } } class App extends PureComponent { render() { return ( <div> App <UserContext.Provider value={{nickname: "why", level: 90, region: "China"}}> <Home/> <About/> </UserContext.Provider> </div> ) } } export default App;
After reading the above code, we will use the high-level components:
import React, { PureComponent } from 'react'; // Define a higher-order component function enhanceRegionProps(WrappedComponent) { return props => { //Here, we can directly share the data of region without passing it separately return <WrappedComponent {...props} region="China"/> } } class Home extends PureComponent { render() { return <h2>Home: {`nickname: ${this.props.nickname} Grade: ${this.props.level} region: ${this.props.region}`}</h2> } } class About extends PureComponent { render() { return <h2>About: {`nickname: ${this.props.nickname} Grade: ${this.props.level} region: ${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;
Finally, we can also combine the two.
import React, { PureComponent, createContext } from 'react'; // Define a higher-order component function withUser(WrappedComponent) { //Here, the shared logic is extracted directly, instead of sharing in each component return props => { return ( <UserContext.Consumer> { user => { return <WrappedComponent {...props} {...user}/> } } </UserContext.Consumer> ) } } // Create Context const UserContext = createContext({ nickname: "default", level: -1, region: "China" }); class Home extends PureComponent { render() { return <h2>Home: {`nickname: ${this.props.nickname} Grade: ${this.props.level} region: ${this.props.region}`}</h2> } } class About extends PureComponent { render() { return <h2>About: {`nickname: ${this.props.nickname} Grade: ${this.props.level} region: ${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: "China"}}> <UserHome/> <UserAbout/> <UserDetail/> </UserContext.Provider> </div> ) } } export default App;
2. Login authentication
We know that login is required when browsing some pages. In order to realize this requirement, we can't add login authentication operations to page components one by one; At this time, we can use the high-level components. We write the authentication operation into the high-level components, so that when export ing the relevant page components that need authentication operation, we can directly use our high-level components.
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; } // Shopping cart components 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. Hijack life cycle function
Higher order components are not used
import React, { PureComponent } from 'react'; class Home extends PureComponent { // About to render get a time beginTime UNSAFE_componentWillMount() { this.beginTime = Date.now(); } // Get another time endTime when rendering is complete componentDidMount() { this.endTime = Date.now(); const interval = this.endTime - this.beginTime; console.log(`Home render time : ${interval}`) } render() { return <h2>Home</h2> } } class About extends PureComponent { // About to render get a time beginTime UNSAFE_componentWillMount() { this.beginTime = Date.now(); } // Get another time endTime when rendering is complete componentDidMount() { this.endTime = Date.now(); const interval = this.endTime - this.beginTime; console.log(`About render time : ${interval}`) } render() { return <h2>About</h2> } } export default class App extends PureComponent { render() { return ( <div> <Home /> <About /> </div> ) } }
Use high-order components (also a common extraction for the operation of obtaining component rendering time)
import React, { PureComponent } from 'react'; function withRenderTime(WrappedComponent) { return class extends PureComponent { // About to render get a time beginTime UNSAFE_componentWillMount() { this.beginTime = Date.now(); } // Get another time endTime when rendering is complete componentDidMount() { this.endTime = Date.now(); const interval = this.endTime - this.beginTime; console.log(`${WrappedComponent.name}render time : ${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> ) } }