React (17) Asynchronous Component

Keywords: React Fragment Webpack Vue

26. Asynchronous components

When using asynchronous components in React, the core knowledge is two:

  1. How webpack loads other modules asynchronously: through require(['xxx'], function(module) {});
  2. How to use the module loaded asynchronously in React: Refer to the normal way of using the module;


[Asynchronous loading]

For asynchronous loading of webpack, check out this article I wrote Asynchronous Loading Actual DEMO.

Simply put, it's require parameter one, which changes from a string to an array, and then parameter two is a callback function. The parameter of the function is the module you load asynchronously.

So getting parameters is equal to getting modules.

[How to use React]

  1. We need to get this module asynchronously.
  2. We can use this module by referring to the use of higher-order components (functions return a class, assign a variable, and then use it as a JSX tag);

Let's start with a simple version:

class RefsDemo extends React.Component {
    constructor() {
        super()
        this.state = {
            myComponent: null
        }
        this.load = this.load.bind(this)
    }

    render() {
        return <div>
            {/* Click Execute load Method */}
            <button onClick={this.load}>Click Load Asynchronous Components</button>
            {/* When a variable exists (non-empty, use tags as JSX's tag signature (the variable has been assigned to an asynchronous module); otherwise use null (i.e., no DOM) */}
            {
                this.state.myComponent ? <this.state.myComponent></this.state.myComponent> : null
            }
        </div>
    }

    load() {
        // This is an asynchronous behavior, so you need to get this module in the callback function.
        require(['./learner.js'], Component => {
            // Assignment to the state variable
            this.setState({
                // Loaded modules are stored in Comment.default (because they are exported through export default)
                myComponent: Component.default
            })
        })
    }
}

The train of thought is:

  1. The state variable myComponent is used to store the module, which is initially empty (neither displayed nor loaded).
  2. When the button is clicked, the module'. / learner.js'is loaded asynchronously, and the module is obtained by calling back function parameters.
  3. Assigning the module to the state variable myComponent triggers the life cycle of the state change (which triggers the render method).
  4. When render is re-rendered, it finds that this.state.myComponent is not false after implicit conversion, so it uses its JSX tag (i.e., the asynchronous component is embedded in the current component);
  5. So the asynchronous component is embedded in the current component, and the asynchronous loading of React component is realized.

Other good understandings, more awkward is the JSX grammar:

{
    this.state.myComponent ? <this.state.myComponent></this.state.myComponent> : null
}

The reason for this is to refer to the first section of this series, [22], that is, when a component is assigned to an object's property (which reflects the myComponent property of the state property of this), so it does not need to be capitalized and can be labeled directly with the variable name.

Advanced - Asynchronous Component Loader:

Question:

  1. The above writing is still too complicated.
  2. The state property is needed to control whether the component is displayed or not.
  3. A variable is needed to store the module, and the variable is used as the tag name in JSX grammar.
  4. Write a load method for loading asynchronous components.

In a word, it is not smart and elegant enough.

Objectives:

  1. Write an asynchronous component loader;
  2. To achieve the following functions:
  3. Give it a function, such as const Learner = resolve => require (['. / learner. js'], resolve), the prototype of this function is above require ('. / learner. js'), Component => {};
  4. Then a variable displayComponent is given to control whether the component is displayed or not.
  5. When displayComponent is set to true for the first time and the component is not loaded, the component is loaded.
  6. In order to prevent components from being loaded repeatedly, the component internal variable this.state.amount is responsible for representing the current component state (not loaded, loaded, loaded);
  7. When the component is displayed: the component is loaded (this.state.component) & the parent component controls whether the component is displayed (this.state.displayComponent);

Therefore, the parent component only needs to do two things:

  1. Pass a load function of asynchronous components after coritization.
  2. A variable displayComponent controls whether the asynchronous component is displayed (automatically loaded when first displayed);

as
Code:

Code in app.js parent component

// Introducing an Asynchronous Component Loader
import AsyncLoad from './asyncLoader.js'

// Asynchronous Component Load Function Packaging
const Leaner = resolve => require(['./learner.js'], resolve)

// Here is the JSX tag for the asynchronous component loader of the render method of the parent component
<AsyncLoad modules={Leaner} displayComponent={this.state.displayComponent}></AsyncLoad>

Code in async Loader.js asynchronous component loader

See the Code Notes for specific explanations

/**
 * Created by Wang Dong on 2018/2/8.
 * QQ: 20004604
 * weChat: qq20004604
 * Asynchronous Loading of Factory Components
 */
import React from "react";

const loadingStatus = {
    notLoaded: 0,
    loading: 1,
    loaded: 2
}

export default class AsyncLoader extends React.Component {
    constructor() {
        super()
        this.state = {
            amount: loadingStatus.notLoaded,  // 0 means unloaded, 1 means loaded, and 2 means loaded. Failure to consider loading (not difficult)
            displayComponent: false,    // Whether to display components
            component: null // Asynchronous components are assigned to this variable
        }
    }

    // Life cycle function, which is triggered when the parent component changes state
    componentWillReceiveProps(nextProps) {
        // If there are no modules, direct error reporting
        if (!nextProps.modules) {
            return console.error('You don't pass values. modules Give [Asynchronous Component Loader]')
        }
        // If the control value is true and the component has not been loaded before (expressed in amount == 0)
        if (nextProps.displayComponent && this.state.amount === 0) {
            console.log('Start loading components')
            // So load the component
            this.setState({
                amount: loadingStatus.loading   // Represents loading
            })
            nextProps.modules(module => {
                if (!module.default) {
                    return console.error('You may load multiple asynchronous components, or the loaded components are not. React Components')
                }
                // Asynchronous assignment to variables corresponding to state
                console.log('Component loaded')
                this.setState({
                    amount: loadingStatus.loaded,   // Loaded
                    component: module.default
                })
            })
        }

        this.setState({
            displayComponent: nextProps.displayComponent
        })
    }

    render() {
        /* <React.Fragment> It's React's package container (similar to Vue's < template > tag) */
        return <React.Fragment>
            {/* The component is displayed only when the component is currently displayed and loaded. */}
            {
                this.state.displayComponent && this.state.component ?
                    <this.state.component></this.state.component> : null
            }
        </React.Fragment>
    }
}

Posted by qadeer_ahmad on Fri, 17 May 2019 13:48:11 -0700