Higher order functions and components

1.1 Higher-order functions

A higher-order function meets at least one of the following conditions:

  1. Accepts one or more functions as input;
  2. Output a function;

Common filter, Map, and Reduce functions in JavaScript are all higher-order functions.

1.2 Advanced components

So what are higher-order components?

  1. Higher-order Components, HOC for short;

  2. A higher-order component is a function that takes a component and returns a new component.

We can parse it as follows:

  1. First, a higher-order component is not itself a component, but a function;
  2. Second, the argument to this function is a component and the return value is a component

The sample

import React, { PureComponent } from 'react'

class App extends PureComponent {
  render() {
    return (
      <div>
        App
      </div>)}}// This is a higher-order function
// Because the parameter passed is a component object, the parameter is capitalized
function enhanceComponent(WrappedComponent) {
  // The higher-order component returns a new component
  return class HighOrderComponent extends PureComponent{
    render() {
      return <WrappedComponent />}}}// Call higher-order components
// Notice that a new component object is passed in, so it is App
// ReactElement 
      
export default enhanceComponent(App)
Copy the code

Omitting component name

In traditional function writing

// function declaration
function fun() {... }// Function expression
// Function is a function of fun
const fun = function fun() {... }// So it can be simplified as
const fun = function() {... }Copy the code

The same goes for the use and definition of classes

class Person{... }// Convert to class expression
cosnt Person = class Person {... }// The Person after class has no value at all
// Can be abbreviated as
constPerson = calss Person {... }Copy the code

So in the writing of higher-order components, can be changed to

import React, { PureComponent } from 'react'

class App extends PureComponent {
  render() {
    return (
      <div>
        App
      </div>)}}function enhanceComponent(WrappedComponent) {
  // You can omit the name of the class component here
  return class extends PureComponent{
    render() {
      return <WrappedComponent />}}}export default enhanceComponent(App)
Copy the code

Alias the component

import React, { PureComponent } from 'react'

class App extends PureComponent {
  render() {
    return (
      <div>
        App
      </div>)}}function enhanceComponent(WrappedComponent) {
   class HighOrderComponent extends PureComponent{
    render() {
      return <WrappedComponent />}}// Every react component (class or function) has a property called displayName that can change the component name
  HighOrderComponent.displayName = 'HOC'

  return HighOrderComponent
}

export default enhanceComponent(App)
Copy the code

Attribute value passing

index.js

ReactDOM.render(<App name="Klaus" />.document.getElementById('root'))
Copy the code

App.js

import React, { PureComponent } from 'react'

class App extends PureComponent {
  render() {
    return (
      <div>
        name: { this.props.name }
      </div>)}}function enhanceComponent(WrappedComponent) {
  return class extends PureComponent{
    // Pass all props in the parent component to the parameter component using an expansion
    render() {
      return <WrappedComponent {. this.props} / >}}}export default enhanceComponent(App)
Copy the code

Higher-order components are not part of the React API. They are design patterns based on the React composite features.

Higher-order components are common in some React third-party libraries:

  1. Like connect in Redux
  2. For example, withRouter in react-Router

Function components are used in higher-order components

function enhanceComponent(WrappedComponent) {
  return function(props){
    return <WrappedComponent {. props} / >}}Copy the code

Second, the use of higher-order components

Props to enhance

Add new props without modifying the original code

Home.js

import React, { PureComponent } from 'react'

export default class About extends PureComponent {
  render() {
    return (
      <div>
          name: {this.props.name}
          <br/>
          age: {this.props.age}
      </div>)}}Copy the code

About.js

import React, { PureComponent } from 'react'

export default class About extends PureComponent {
  render() {
    return (
      <div>
          name: {this.props.name}
          <br/>
          age: {this.props.age}
      </div>)}}Copy the code

App.js

import React, { PureComponent } from 'react'

import Home from './Home'
import About from './About'

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home name='Klaus' age={23} />
        <hr/>
        <About name="Steven" age={25} />
      </div>)}}Copy the code

Requirement: Add a common property for the Home and About components called region=” China “,

If you add directly to the component, you need to modify it everywhere you use the Home and About components,

Region can then be set in HOC

The modified code is as follows:

App.js

import React, { PureComponent } from 'react'

import Home from './Home'
import About from './About'

export default class App extends PureComponent {
  render() {
    return (
      <div>
        <Home name='Klaus' age={23} />
        <hr/>
        <About name="Steven" age={25} />
      </div>)}}Copy the code

Home.js

import React, { PureComponent } from 'react'
import {enhanceProps} from './HOC.js'

class Home extends PureComponent {
  render() {
    return (
      <div>
          name: {this.props.name}
          <br/>
          age: {this.props.age}
          <br/>
          ergion: {this.props.region}
      </div>)}}// Use higher-order functions to hijack the original component and then perform a secondary operation
export default enhanceProps(Home)
Copy the code

About.js

import React, { PureComponent } from 'react'
import {enhanceProps} from './HOC.js'

class About extends PureComponent {
  render() {
    return (
      <div>
          name: {this.props.name}
          <br/>
          age: {this.props.age}
          <br/>
          ergion: {this.props.region}
      </div>)}}export default enhanceProps(About)
Copy the code

HOC.js

import React from 'react'

export function enhanceProps(Cpn) {
  return props= > <Cpn {. props} region="china" />
}
Copy the code

The Shared context

You can use higher-order components to share the Context, avoiding multiple calls to the Consumer component

App.js

import React, { PureComponent } from 'react'
import {UserCtx} from './context'

import Home from './Home'
import About from './About'

export default class App extends PureComponent {
  render() {
    return (
      <UserCtx.Provider value={{ name: 'Steven', age: 18}} >{/* If the Provider is used, the default value of the Provider property is not fetched. If the Provider is not provided, the default value is fetched.<Home />
          <hr/>
          <About/>
      </UserCtx.Provider>)}}Copy the code

Home.js

import React, { PureComponent } from 'react'
import {withUser} from './HOC';

class Home extends PureComponent {
  render() {
    return (
      <div>
        name: {this.props.name}
        age: {this.props.age}
      </div>)}}export default withUser(Home)
Copy the code

About.js

import React, { PureComponent } from 'react'
import {withUser} from './HOC'

class About extends PureComponent {
  render() {
    return (
      <ul>
        <li>name: {this.props.name}</li>
        <li>age: {this.props.age}</li>
      </ul>)}}export default withUser(About)
Copy the code

HOC.js

import React from 'react'
import { UserCtx } from './context'

// Use UserContext so name withUser
export function withUser(Cpn) {
  return props= > (
    <UserCtx.Consumer>
       {
         user =><Cpn {. props} {. user} / >
       }
    </UserCtx.Consumer>)}Copy the code

context.js

import { createContext } from 'react'

export const UserCtx = createContext({
  name: 'Klaus'.age: 18
})
Copy the code

Unified operation

In real development, you might need to do some of the same things

For example: sending the same network request, loading animation, authentication (permission authentication) operation

import React, { PureComponent, Fragment } from 'react'

// define a page element to display the contents of the page, named XXXPage
// When defining a pure component, name it XXXCpn
function LoginPage() {
  return <h2>LoginPage</h2>
}

function CartPage() {
  return <h2>CartPage</h2>
}

function ProfilePage() {
  return <h2>ProfilePage</h2>
}

// Authentication operation
function withAuth(WrapCpn){
  const Auth =  props= > {
    const { isLogin } = props

    return isLogin ? <WrapCpn {. props} / > : <LoginPage {. props} / >
  }

  // The component of the function has no component name, so the component displayed in the component tree is named Anonymous
  // There is a displayName on the component object to change the name of the component (class not instance)
  Auth.displayName = `${WrapCpn.name}Auth`

  return Auth
}

// Multiple pages may require authentication
const ProfileAuth = withAuth(ProfilePage)
const CartAuth = withAuth(CartPage)

export default class App extends PureComponent {
  render() {
    return (
      <Fragment>
        <ProfileAuth isLogin={true} />
        <CartAuth isLogin={false} />
        {/*
          =>
            ProfilePage
            LoginPage
        */}
      </Fragment>)}}Copy the code

Hijack the life cycle -- a mixin-like effect

import React, { PureComponent, Fragment } from 'react'

class Home extends PureComponent {
  render() {
    return <h2>Home</h2>}}class Page extends PureComponent {
  render() {
    return <h2>Page</h2>}}function calcRenderTime(WrapCpn) {
  return class Cpn extends PureComponent {
    constructor(props) {
      super(props)

      this.state = {
        beginTime: 0.endTime: 0}}UNSAFE_componentWillMount() {
      this.beginTime = Date.now()
    }

    componentDidMount() {
      this.endTime = Date.now()
      // Every class and function in ES is a read-only name attribute that represents the name of the current class or function
      console.log(`${WrapCpn.name}Is renderTimeThe ${this.endTime - this.beginTime}ms`)}render() {
      return <WrapCpn />}}}const HomeTime = calcRenderTime(Home)
const PageTime = calcRenderTime(Page)

export default class App extends PureComponent {
  render() {
    return (
      <Fragment>
        <HomeTime />
        <PageTime />
      </Fragment>)}}Copy the code

In React, when components are used, use component combinations instead of inheriting components

Meaning of higher order functions

In fact, early React provided mixins for reuse between components, which are no longer recommended:

  • Mixins can be interdependent and coupled to each other, which is not conducive to code maintenance
  • Methods in different mixins can conflict with each other
  • There are so many mixins that components can sense and even deal with them, which can snowball in code complexity

Of course, HOC has some drawbacks of its own:

  • HOC needs to be wrapped or nested on top of the original components, and if you use it a lot, you get a lot of nesting, which makes debugging very difficult;

  • HOC can hijack props and can cause conflict if conventions are not followed;

    • For example, there was an attribute called nickName in the props, but the nickName attribute was extended during the extension. As a result, the nickName attribute was overwritten by the later attribute

      This results in inconsistencies between the displayed data and the incoming data

      <Home name='Klaus' age={23} / >export function enhanceProps(Cpn) {
        return props= > <Cpn {. props} name="smo" region="china" />
      }
      
      // => name: smo
      // age: 23
      // ergion: china
      Copy the code

Hooks were groundbreaking in that they solved many of the problems that existed before React

  • Such as this pointing problems, such as hoc nesting complexity problems, etc.;

Iii. Forwarding of REF

Ref can’t be applied to functional components, as we saw earlier when we studied ref:

  • Because the functional component has no instance, the corresponding component object cannot be retrieved

However, during development, we might want to retrieve the DOM of an element in a functional component. How do we do this?

  • Method 1: Pass the REF attribute directly (wrong)
  • Method 2: through the forwardRef function;
import React, { PureComponent, createRef, forwardRef } from 'react'

// With the forwaRef wrapper, it passes a ref object in the second argument of the function component,
// this is the instance of the ref defined at the time of the call
// Use ref to access the corresponding element inside the function component
const Cpn = forwardRef(function(props, ref) {
  return <h3 ref={ref}>Cpn</h3>
})


export default class App extends PureComponent {
  constructor(props) {
    super(props)

    this.titleRef = createRef()
    this.cpnRef = createRef()
  }

  render() {
    return (
      <div>
        <h2 ref={this.titleRef}>title</h2>{/* By default, function components do not have instance objects or lifecycle hooks, so refs cannot be used on function components. Refs are functions managed internally, and cannot be passed as props.<Cpn ref={this.cpnRef} />
        <button onClick={e= > this.printRef()} >print</button>
      </div>)}printRef() {
    console.log(this.titleRef.current)
    console.log(this.cpnRef.current)
  }
}

Copy the code

4. Simple use of Protals

In some cases, we want to render content independent of the parent component, and even independent of the DOM element currently mounted (by default, it is mounted to the DOM element with the ID root).

In general, when you return an element from the render method of a component, the element will be mounted to its nearest parent in the DOM node:

However, sometimes it is beneficial to insert child elements in different places in a DOM node (for example, a Modal component) :

Portal provides an excellent solution for rendering child nodes to DOM nodes that exist outside the parent component:

  • The first argument (child) is any renderable React child, such as an element, string, or fragment;
  • The second argument (container) is a DOM element;

The sample

Requirement: We are going to develop a Modal component that renders its children to the middle of the screen

import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'

class Modal extends PureComponent {
  render() {
    // To get all the children of the current element, call this.props. Children
    return ReactDOM.createPortal(
      this.props.children,
      document.getElementById('modal'))}}export default class App extends PureComponent {
  render() {
    return (
      <div>Basic use of Protal<Modal>
          <h3>Modal components</h3>
         </Modal>
      </div>)}}Copy the code

The essence of Protoal is to help react-DOM help us call the react. render method, render to non-parent elements

Five, the fragments

In previous development, we always wrapped a div element around content returned from a component:

If we want to render a div without rendering it, how do we do that?

  • Using the fragments
  • Fragments allow you to group sublists without adding extra nodes to the DOM
import React, { PureComponent, Fragment } from 'react'

export default class App extends PureComponent {
  render() {
    return (
      <Fragment>
        <h2>Klaus</h2>
        <h3>23</h3>
      </Fragment>)}}Copy the code

Simplified (it looks like an empty tag)

import React, { PureComponent, Fragment } from 'react'

export default class App extends PureComponent {
  render() {
    return (
      <>
        <h2>Klaus</h2>
        <h3>23</h3>
      </>)}}Copy the code

In the simplified version, you cannot add any attributes to it (such as key).

Adding a key attribute to a Fragment in a loop works,

But don’t add styles to them, because fragments don’t end up rendering as a real DOM element

So the styles you add to it won’t work either

Six, StrictMode

StrictMode is a tool for highlighting potential problems in your application.

  • Like fragments, StrictMode does not render any visible UI;
  • It triggers additional checks and warnings for its descendant elements, but does not affect the normal execution of the code;
  • Strict mode checking runs only in development mode; They do not affect production builds

StrictMode is essentially a validation tool that prevents us from using outdated or deprecated apis or syntax

Strict mode can be enabled for any part of the application:

  • No strict schema checking is run on Header and Footer components;

  • However, ComponentOne and ComponentTwo and all their descendants will be checked;

The sample

import React, { PureComponent, StrictMode } from 'react'

class Home extends PureComponent {
  // There is a warning
  UNSAFE_componentWillMount() {
    console.log('Home')}render() {
    return <h2>Home</h2>}}class Profile extends PureComponent {
  // There is no warning
  UNSAFE_componentWillMount() {
    console.log('Profile')}render(){
    return <h3>Profile</h3>}}export default class App extends PureComponent {
  render() {
    return (
      <div>
        <StrictMode>
          <Home />
        </StrictMode>
        <Profile />
      </div>)}}Copy the code

What does strict mode check for?

  1. Identify unsafe lifecycles:

    • For example,componentWillMount
  2. Use the outdated REF API

    • For example,ref="title"
  3. Using the deprecated findDOMNode method (understood)

    • In the React API, you could get the DOM from findDOMNode, but this is no longer recommended
    • Now recommendedref
  4. Check for unexpected side effects

    • Some of the component’s lifecycle methods (e.g., constructor) are called twice;

    • This is done deliberately in strict mode to let you see if some logic written here has any side effects (bugs or performance losses) when called multiple times;

    • In a production environment, it is not called twice;

  5. Detecting stale Context APIS (understand)

    • Context is used by declaring the properties of the Context object through the static property, returning the Context object through getChildContext, etc.

    The basics of using styles in React and controlled components in the previous article