Come and join us!

“The Newbies of Little and Hill” provides technical information and a series of basic articles for front-end developers. For a better user experience, please move to our official website of Xiaoheshan beginners (https://xhs-rookies.com/) to learn, timely access to the latest articles.

“Code tailor”, if you are interested in our articles or would like to make some suggestions, please follow our official account “newbies of Xiaoheshan” at WeChat, and contact us. You can also view our articles on WeChat. Every suggestion or agreement is a great encouragement to us!

preface

In this section we will introduce higher-order components in React, what higher-order components are for, and how they complement higher-order components.

This article will introduce you to the following:

  • Recognize higher-order components
  • Use of higher-order components
  • Meaning of higher-order components
  • Points of note for higher-order components
  • Forwarding in higher-order componentsrefs
  • Portals
  • Fragment
  • STRICT MODE –StrictMode

High order component

Recognize higher-order components

What are higher-order components? I’m sure many of you have heard of it, you use higher order functions, and they’re very similar, so we can just review what a higher order function is.

Wikipedia definition of a higher-order function: satisfies at least one of the following conditions:

  • Take one or more functions as input;
  • Output a function;

Filter, map, and reduce, which are common in JavaScript, are all higher-order functions.

So what are higher-order components?

  • High order component isHigher-Order Components, referred to asHOC, it isReactAn advanced technique for reusing component logic in.
  • A higher-order component is a function that takes a parameter to a component and returns a value to a new component.

From this, we can analyze:

  • A higher-order component is not itself a component, but a function
  • The argument to this function is a component, and the return value is also a component

The invocation process for a higher-order component looks something like this:

const EnhancedComponent = higherOrderComponent(WrappedComponent)

Components convert props to UI, and higher-order components convert components to another component.

The process of writing a higher-order function looks something like this:

  • Returns a class component suitable for stateful processing that uses a lifecycle
function higherOrderComponent(WrapperComponent) {
  return class NewComponent extends PureComponent {
    render() {
      return <WrapperComponent />
    }
  }
}
  • Returns a function component suitable for simple logical processing
function higherOrderComponent(WrapperComponent) {
  return (props) => {
    if (props.token) {
      return <WrapperComponent />
    } else {
      return <></>
    }
  }
}

In ES6, it is possible to omit the class name from a class expression, so we write it this way:

function higherOrderComponent(WrapperComponent) {
  return class extends PureComponent {
    render() {
      return <WrapperComponent />
    }
  }
}

The component name can be changed by displayName:

function higherOrderComponent(WrapperComponent) {
  class NewComponent extends PureComponent {
    render() {
      return <WrapperComponent />
    }
  }
  NewComponent.displayName = 'xhsRookies'
  return NewComponent
}

Note:High-order components are not
React APIPart of it is based on
ReactThe design pattern formed by the combination characteristics of;

So, what can higher-order components help us do in our development? Read on!

Use of higher-order components

The props to enhance

1. Add new props properties without changing the original code

Suppose we have the following case:

class XhsRookies extends PureComponent { render() { const { name, age } = this.props return <h2>XhsRookies {name + age}</h2> } } export default class App extends PureComponent { render()  { return ( <div> <XhsRookies name="xhsRookies" age={18} /> </div> ) } }

If we need to add a height property to the props of an XHSRookies component, we can do this:

class XhsRookies extends PureComponent { render() { const { name, age } = this.props return <h2>XhsRookies {name + age}</h2> } } function enhanceProps(WrapperComponent, newProps) { return (props) => <WrapperComponent {... props} {... newProps} /> } const EnhanceHeader = enhanceProps(XhsRookies, { height: 1.88}) export default class App extends PureComponent {render() {return (<div> < enhanceHeader name="xhsRookies" age={18} /> </div> ) } }

Use higher-order components to share the Context

Import React, {PureComponent, CreateContext} from 'React' const UserContext = CreateContext ({NICKNAME: 'Default ', Level: -1, }) function XhsRookies(props) { return ( <UserContext.Consumer> {(value) => { const { nickname, Level} = value return <h2>Header {' nickname :' + NICKNAME + 'level'}</h2>}} </UserContext.Consumer>)} export default class App extends PureComponent { render() { return ( <div> <UserContext.Provider value={{ nickname: 'xhsRookies', level: 99 }}> <XhsRookies /> </UserContext.Provider> </div> ) } }

We define a high-level component, ShareContextHoc, to share the Context

Import React, {PureComponent, CreateContext} from 'React' const UserContext = CreateContext ({NICKNAME: 'Default ', Level: 1, }) function ShareContextHOC(WrapperCpn) { return (props) => { return ( <UserContext.Consumer> {(value) => { return <WrapperCpn {... props} {... value} /> }} </UserContext.Consumer> ) } } function XhsRookies(props) { const { nickname, Function Footer(props) {const {nickname, props return <h2>Header {" props :' + NICKNAME + '" + level}</h2>} function Footer(props) {const {NICKNAME, Level} = props return <h2>Footer {" nickname :' + NICKNAME + '+ level}</h2>} const newxhsRookies = ShareContextHOC(Header) export default class App extends PureComponent { render() { return ( <div> <UserContext.Provider  value={{ nickname: 'xhsRookies', level: 99 }}> <NewXhsRookies /> </UserContext.Provider> </div> ) } }

Rendering judgment authority

In development, we encountered the following scenarios:

  • Some pages must be logged in successfully
  • If the user does not login successfully, jump directly to the login page

In this scenario, we can use higher-order components to complete the authentication:

Function loginPage () {return <h2> loginPage </h2>} from HomePage() {return <h2>HomePage</h2>} from HomePage() {return <h2>HomePage</h2>} from HomePage();  export default class App extends PureComponent { render() { return ( <div> <HomePage /> </div> ) } }

Using the authentication component:

import React, {pureComponent} from 'react' function loginAuthority(Page) {props (props) => {if (props. IsLogin) {// Return success Page if login succeeds Return <Page />} else {return < loginPage />}} function loginPage () {return <h2> loginPage </h2> } function HomePage() { return <h2>HomePage</h2> } const AuthorityPassPage = loginAuthority(HomePage) export default class App extends PureComponent { render() { return ( <div> <AuthorityPassPage isLogin={true} /> </div> ) } }

Life cycle hijacking

When multiple components need to do something in their life cycle, and these things are all of the same logic, we can use higher-order components to uniformly help these components to complete the work, as follows:

import React, { PureComponent } from 'react' class Home extends PureComponent { componentDidMount() { const nowTime = Date.now() Console. log(' Home :${nowTime} ')} render() {return (<div> <h2>Home</h2> <p>)} class Detail extends PureComponent {ComponentDidMount () {const nowTime = date.now () console.log(' Detail render time :${nowTime} ')} Render () {return (<div> <h2>Detail</h2> <p> <p> </div>)} export default class App extends PureComponent { render() { return ( <div> <Home /> <Detail /> </div> ) } }

We can use high-order rentals to help complete the ComponentDidMount lifecycle function of the Home component and Detail component:

import React, { PureComponent } from 'react' function logRenderTime(WrapperCpn) { return class extends PureComponent { ComponentDidMount () {const nowTime = date.now () console.log(' ${wrapPercepp.name} Render () {return <WrapperCpn {... This.props} />}}} class Home extends PureComponent {render() {return (<div> <h2>Home</h2> <p>) </p> </div>)}} class Detail extends PureComponent {render() {return (<div> <h2>Detail</h2> <p>) </p> </div> ) } } const LogHome = logRenderTime(Home) const LogDetail = logRenderTime(Detail) export default class App extends PureComponent { render() { return ( <div> <LogHome /> <LogDetail /> </div> ) } }

Meaning of higher-order components

By using higher-order components in the different situations above, we can see that some React code can be handled more elegently with higher-order components.

In fact, one of the early ways React provides reuse between components is mixins, which are no longer recommended:

  • MixinIt can be interdependent and coupled, which is bad for code maintenance
  • differentMixinThe methods in “may conflict
  • MixinVery often, components are perceived and even processed, which adds a snowballing complexity to the code

Of course, Hoc has some downsides of its own:

  • HOCNeed to wrap or nest the original component, if used in large quantitiesHOC, there will be a lot of nesting, which makes debugging very difficult;
  • HOCCan be hijackedprops, in the case of non-compliance with the agreement may also cause conflicts;

Reasonable use of higher-order components will be a great help to our development.

Points of note for higher-order components

Do not use Hoc in the render method

The React diff algorithm (called coordination) uses the component identity to determine whether it should update an existing subtree or drop it and mount a new one. If the component returned from Render is the same as the component in the previous render (===), React recursively updates the subtree by differentiating it from the new one. If they are not equal, the previous subtree is completely unmounted.

Usually, you don’t need to think about this. This is important for Hoc, though, because it means that you should not apply Hoc to a component in its render method:

Render () {// Each call to render creates a new EnhancedComponent // EnhancedComponent1! == EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); // This will cause the subtree to be unmounted and remounted every time it is rendered! return <EnhancedComponent />; }

This is not just a performance issue – remounting a component causes the state of that component and all of its children to be lost.

If Hoc is created outside of the component, then the component will only be created once. Therefore, each render will be the same component. Generally speaking, this is in line with your expected performance.

const EnhancedComponent = enhance(MyComponent) class App extends PureComponent { render() { return <EnhancedComponent /> }}

In rare cases, you need to call HOC dynamically. You can call it in a component’s lifecycle method or in its constructor.

Refs will not be passed

Although the convention for higher-order components is to pass all props to the wrapped component, this does not apply to refs. That’s because ref isn’t actually a prop, just like key, which is specifically handled by React. If you add Ref to Hoc’s return component, the Ref reference points to the container component rather than the wrapped component.

Complement of components

Refs are forwarded in the higher-order component

We mentioned earlier that in higher-order components, refs will not be passed, but we may encounter the need to forward refs in higher-order components. How can we solve this problem? Fortunately, we can use the React. ForwardRef API to help solve this problem.

Let’s start with a Hoc example that output components props to the console:

function logProps(WrappedComponent) { class LogProps extends React.Component { componentDidUpdate(prevProps) { console.log('old props:', prevProps) console.log('new props:', this.props) } render() { return <WrappedComponent {... this.props} /> } } return LogProps }

LogProps Hoc perforates all props to its wrapped components, so the render result will be the same. For example, we could use this Hoc to record all props passed to the “Fancy Button” component:

class FancyButton extends React.Component { focus() { // ... } / /... } // We export LogProps instead of fancyButton. // Although it also renders a FancyButton. export default logProps(FancyButton)

Until now, as stated in the previous example, refs will not pass through. If you add a ref to HOC, the ref will refer to the outermost container component, not the wrapped component.

Import fancyButton from './ fancyButton 'const ref = react. createRef() // The fancyButton component we import is high-order (HOC) LogProps. // Even though the render results will be the same, // our ref will point to LogProps instead of the internal FancyButton component! // This means that we cannot call methods such as Ref.Current. Focus (); <FancyButton label="Click Me" handleClick={handleClick} ref={ref} />

At this point, we can explicitly forward refs to the internal FancyButton component using the React. ForwardRef API. React. ForwardRef takes a render function that receives props and ref parameters and returns a React node.

function logProps(Component) { class LogProps extends React.Component { componentDidUpdate(prevProps) { console.log('old  props:', prevProps) console.log('new props:', this.props) } render() { const { forwardedRef, ... Rest} = this.props // Define the custom prop property "forwardRef" to Ref Return <Component Ref ={forwardRef} {... Rest} />}} // Note the second parameter "ref" of the React. ForwardRef callback. // We can pass this as a regular prop property to LogProps, such as "forwardRef". It can then be mounted to the child components wrapped up with LogProps. return React.forwardRef((props, ref) => { return <LogProps {... props} forwardedRef={ref} /> }) }

This allows us to pass refs in higher-order components.

Portals

In some cases, we want to render content independent of the parent component, or even independent of the DOM element that is currently mounted (by default, on the DOM element with the id root).

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

  • The first parameter (child) is anything renderableReactChild elements, such as an element, a string, orfragment;
  • The second parameter (container) is aDOMElements;
ReactDOM.createPortal(child, container)

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

Render () {// React mounts a new div and renders the child element return (<div> {this.props. Children} </div>); }

However, there are times when it can be beneficial to insert child elements at different locations within a DOM node:

Render () {// React and * does not * create a new div. It simply renders the child element into 'domNode'. // 'domNode' is a valid DOM node that can be anywhere. return ReactDOM.createPortal( this.props.children, domNode ); }

For example, let’s develop a TabBar component that renders its children to the top of the screen:

  • Step 1: Reviseindex.htmlAdd a new node
<div id="root"></div> <! -- new node --> <div id="TabBar"></div>
  • Step 2: Style the node
#TabBar {
  position: fixed;
  width: 100%;
  height: 44px;
  background-color: red;
}
  • Step 3: Write component code
import React, { PureComponent } from 'react' import ReactDOM from 'react-dom' class TabBar extends PureComponent { constructor(props) { super(props) } render() { return ReactDOM.createPortal(this.props.children, document.getElementById('TabBar')) } } export default class App extends PureComponent { render() { return ( <div> <TabBar> </div> </div>)}

Fragment

In previous development, we always wrapped a div element when returning content from a component:

Export default class App extends PureComponent {render() {return (<div> <h2>) {render() {return (<div> <h2>); </h2> </button> </button> </button> </div>)}

The renderings

We’ll notice that there is an extra div element:

  • thisdivThe element is required for some scenarios (for example, we want to put one indivElement, and then set the style accordingly.
  • In some casesdivIt’s not necessary, for example here I might want all of the content to be rendered directlyrootIn can;

When we delete the div, we get an error. What should we do if we want to not render the div?

  • useFragment
  • FragmentAllows you to group sublists without having toDOMAdd additional nodes;
Export Default class App extends PureComponent {render() {return (<Fragment> <h2> WeChat); </h2> </button> thumb up </button> </button> </Fragment>)}}

The rendering effect is as follows:

React also provides Fragments

It looks like the empty tag <>

Export default class App extends PureComponent {render() {return (<> <h2>); Small and mountain rookie < / h2 > < button > thumb up < / button > < button > attention < / button > < / a >)}}

Note:If we need to be in
FragmentAdd properties to, for example
key, we will not be able to use segmental syntax

StrictMode

StrictMode is a tool used to highlight potential problems in your application, and like Fragment, StrictMode does not render any visible UI. It triggers additional checks and warnings for its descendant elements.

Note:Strict schema checking runs only in development mode; They do not affect the production build.

You can enable strict mode for any part of your application. Such as:

import React from 'react'

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  )
}

In the example above, strict schema checking is not run on the Header and Footer components. However, ComponentOne and ComponentTwo and all their descendant elements are checked.

StrictMode currently helps:

  • Identify insecure lifecycles
  • About the use of wastefindDOMNodeMethod warnings
  • Detect for unexpected side effects
  • Detect out of datecontext API
  • About using out-of-date stringsref APIThe warning

1. Identify insecure life cycles

Some outdated lifecycle methods are not safe to use in asynchronous React applications. However, if your application uses third-party libraries, it is difficult to ensure that they do not use these lifecycle methods.

When strict mode is enabled, React lists all Class components that use unsafe life cycle methods and prints a warning message containing information about those components, as follows:


Warning about using obsolete string ref API

Previously, React provided two ways to manage REFs:

  • An obsolete stringref APIIn the form of
  • The callback functionAPIIn the form.

Although the string ref API is more convenient to use between the two, it has some disadvantages, so the official recommendation is to use callbacks.

React 16.3 adds a third option that provides the convenience of using string ref without any disadvantages:

class MyComponent extends React.Component {
  constructor(props) {
    super(props)

    this.inputRef = React.createRef()
  }

  render() {
    return <input type="text" ref={this.inputRef} />
  }

  componentDidMount() {
    this.inputRef.current.focus()
  }
}

Since the object ref was added primarily to replace the string ref, strict mode now warns against using the string ref.


Warning about using the deprecated findDomNode method

React supports using FindDomNode to search for DOM nodes in the tree for a given class instance. Usually you don’t need to do this, because you can bind ref directly to the DOM node, but since this method has been deprecated, I won’t go into details here, but you can learn for yourself if you’re interested.


4. Detect unexpected side effects

  • For this componentconstructorIt’s going to be called twice;
  • This is a deliberate operation in strict mode to let you see if some of the logical code you wrote here produces any side effects when it is called multiple times;
  • In a production environment, it is not called twice;
class Home extends PureComponent {
  constructor(props) {
    super(props)

    console.log('home constructor')
  }

  UNSAFE_componentWillMount() {}

  render() {
    return <h2 ref="home">Home</h2>
  }
}

5. Detect stale context API

In the early days, contexts were used by declaring the properties of the Context object static, and by returning the Context object via getChildContext. However, this approach is currently obsolete, and the outdated Context API is error-prone and will be removed in future major releases. It still works in all 16.x versions, but in strict mode, the following warning will be displayed:

Next day forecast

In this section, we learned about higher-order components in React and what they add to it. In the next chapter, we will start learning React-Router.