The introduction

I have been using react.js for some time, and I have recorded the small pits and tips encountered in the process of using it, hoping to help others. This article is a long one. You can only grow if you can resist loneliness and temptation.

I. Tools

1. Display HTML

<div dangerouslySetInnerHTML={{ __html: LANG.auth_register_tips1 }}/>Copy the code

2. Common components

  • axios(httpRequest module, can be used for any front-end scenario, very powerful)
  • echarts-for-react(Visual chart, someone else’s React based encapsulation of echarts, is good enough)
  • recharts(Another is based onreactEncapsulated chart)
  • nprogress(Top loading bar, nice to use)
  • react-draft-wysiwyg(Based on the othersreactRich text encapsulation, if found other better can be replaced)
  • react-draggable(Drag and drop modules to find a simpler version)
  • screenfull(Full screen plug-in)
  • photoswipe(Image shell layer view plugin, does not rely on jQuery, still pretty good)
  • animate.css(CSS Animation Library)
  • redux WebAn application is a state machine, and views correspond to states one to one. All state is stored in one object
  • redux-loggerThe log
  • ReselectMemory components
  • redux-thunk To solve the asynchronous action problem
  • redux-sagaTo solve the asynchronous action problem
  • react-router-redux The route is synchronized with the application state
  • react-router-dom

React-devtools debugger

Tool address: github.com/facebook/re…

Global installation:

yarn global add react-devtoolsCopy the code

Configuration: Configure it in package.json:

"scripts": {
"devtools": "react-devtools"
}Copy the code

Then start yarn Run devtools

Ii. Component communication

Situations in which components are required to communicate

  • The parent component communicates with the child component
  • The child communicates with the parent
  • Cross-level component communication
  • Communication between components with no nested relationship
  1. Redux architecture
  2. Parent component to child component — props
  3. The child component receives parameters from the parent component — props. Funciton
  4. Leverage the event mechanism

1. The parent component communicates with the child component

React data flow is unidirectional, with parent to child communication being the most common; The parent component uses props to pass the required information to the child component

2. Child components communicate with parent components

  • Use callback functions
  • Utilize the custom event mechanism

The child component changes the state of the parent component

Const {data} = this.state; this.setState({ data: {... data, key: 1 } }); This.setstate (({data}) => ({data: {... data, key: 1 } })); This.setstate ((state, props) => {return { counter: state.counter + props.step };
});Copy the code

3. Cross-level communication between components

  • Pass props to multiple components

For example, to communicate between component A and component B, the common parent component of A and B is found first. Component A communicates with component C first, and component C communicates with component B through props. In this case, component C acts as the middleware

  • Use the context

Context is a global variable, like a big container that can be accessed anywhere, so we can put the information that we want to communicate on the context, and then we can get it from other components; React officially does not recommend using a lot of context, although it can reduce layer passing, but when the component structure is complex, we don’t know where the context is coming from; And context is a global variable, and global variables are the ones that cause the application to get messy in the first place.

Use the context

ListItem is a child of List, which is a child of app

ListItem.js

import React, { Component } from 'react';
import PropTypes from 'prop-types'; Class ListItem extends Component {/ / subcomponents declare themselves to use the context static contextTypes = {color: PropTypes.string, } static propTypes = { value: PropTypes.string, }render() {
        const { value } = this.props;
        return( <li style={{ background: this.context.color }}> <span>{value}</span> </li> ); }}export default ListItem;Copy the code

List.js

import ListItem from './ListItem'; Class List extends Component {static childContextTypes = {color: = ""; color: =" "; Proptypes. string,} static PropTypes = {list: proptypes. array,} // Provide a function that returns the appropriate contextgetChildContext() {
        return {
            color: 'red'}; }render() {
        const { list } = this.props;
        return (
            <div>
                <ul>
                    {
                        list.map((entry, index) =>
                            <ListItem key={`list-${index}`} value={entry.text} />, ) } </ul> </div> ); }}export default List;Copy the code

App.js

import React, { Component } from 'react';
import List from './components/List';
const list = [
    {
        text: 'Topic one',
    },
    {
        text: 'Problem two',},];export default class App extends Component {
    render() {
        return( <div> <List list={list} /> </div> ); }}Copy the code

4. Communication between components without nested relationships

  • Use a custom event mechanism

In the componentDidMount event, subscribe to the event once the component has been mounted. Unsubscribe from componentWillUnmount event during component unmount. For a common publish/subscribe pattern, borrow the browser version of the Node.js Events module

Using custom events

In the following example, List1 and List2 do not have any nested relationship. App is their parent.

Implement the ability to change the display of information in List1 by clicking a button in List2

First you need to install the Events package in your project:

npm install events --saveCopy the code

Create an events.js under SRC in a new util directory

import { EventEmitter } from 'events';
export default new EventEmitter();Copy the code

list1.js

import React, { Component } from 'react';
import emitter from '.. /util/events';
class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            message: 'List1'}; }componentDidMount() {// Declare a custom event when the component is loaded. This.eventemitter = Emitter.addListener ();'changeMessage', (message) => {
            this.setState({
                message,
            });
        });
    }
    componentWillUnmount() {
        emitter.removeListener(this.eventEmitter);
    }
    render() {
        return( <div> {this.state.message} </div> ); }}export default List;Copy the code

List2.js

import React, { Component } from 'react';
import emitter from '.. /util/events';
class List2 extends Component {
    handleClick = (message) => {
        emitter.emit('changeMessage', message);
    };
    render() {
        return (
            <div>
                <button onClick={this.handleClick.bind(this, 'List2')}> Click I change List1 component display information </button> </div>); }}Copy the code

APP.js

import React, { Component } from 'react';
import List1 from './components/List1';
import List2 from './components/List2';
export default class App extends Component {
    render() {
        return( <div> <List1 /> <List2 /> </div> ); }}Copy the code

Custom events are a typical publish-subscribe pattern that enables communication between components by adding listeners to event objects and firing events

OnRef method for communication between components

There is also an onRef method for inter-component communication in addition to props, but the React official documentation recommends against relying too much on the REF. This article uses the onRef context to extract the common components at form entry and the form information at submission.

In the following demo, click the parent component button to obtain all the information of the child component, including the status and method. You can see the console print in the Demo.

Class Parent extends React.Component {test} handleClick=()=>{alert(this.child.state.info) // -> {alert(this.child.state.info) }}}}}}}}}render() {
    return<div> <Child onRef={this.testref} /> <button onClick={this.handleclick}> Parent button </button> </div>}} // Child class Child extends React.Component { constructor(props) { super(props) this.state = { info:'Click on the child component button ha ha ha'}}componentDidMount(){this.props. OnRef (this) console.log(this) // -> pass child to this.props. OnRef ()} handleChildClick=()=>{this.props. this.setState({info:'Get child information via parent button la la la'})}render() {return<button onClick={this.handlechildclick}> Child component button </button>}}Copy the code

Principle:

When the onRef function is called in a child component, the function passed from the parent component is being called. This.props. OnRef (this) The argument here refers to the child component itself, and the parent component receives the reference as its first argument: onRef = {ref => (this.child = ref)} then it uses this.child to hold the reference. Later, the entire child component instance can be accessed within the parent component and child component functions can be called.

Iii. Routing section

With a single-page app built with React, the first thing you want to do to jump between pages is to use routing. React-router and React-router-dom are two packages commonly used in React. This paper mainly explains react-router-DOM.

1. React Router has three types of components:

  • Router components (BrowserRouter, HashRouter)
  • Route Matching component (Route, Switch)
  • Navigation component (Link)

For a React Router based Web application, the root component should be a Router component (BrowserRouter, HashRouter). In the project, react-router-DOM provides and two routes. Both routes create a History object. If our application has a server that responds to web requests, we usually use the

component; If we are using a static file server, we should use the

component

HashRouter and BrowserRouter

There are two types of routing hash and history (if you don’t know the difference between the two modes).

And these two components are the container for routing and must be in the outermost layer

// hashMode reactdom.render (<HashRouter> <Route path="/" component={Home}/>
    </HashRouter>
)
// historyMode reactdom. render(<BrowserRouter> <Route path="/" component={Home}/>
    </BrowserRouter>
)Copy the code

Let’s talk about the parameters on HashRouter and BrowserRouter

  • For example, if you want to deploy your project to www.xxxx.com/web, set basename=”/web”
  • GetUserConfirmation is used to intercept the Prompt component and decide whether to jump
  • ForceRefresh is used to set whether to force the entire browser refresh. The default value is false
  • KeLength is used to set the length of the location.key. The default is 6 and can be customized

3, the Route

A Route is the raw material of a Route. It is the component that controls the display of the path

The parameters of the Route

  • Path Indicates the jump path
  • Component Component displayed in the corresponding path
  • Render can write its own render function to return the specific DOM without setting the Component
  • Location passes a route object, compares it to the current route object, and jumps if it matches
  • Exact Specifies the matching rule. True specifies the exact matching rule.
path url Whether open Matching results
/a /a/b false yes
/a /a/b true no
  • Sensitive Whether path is case-sensitive
path url Whether open Matching results
/a /a true yes
/a /A true yes
  • Strict Specifies whether to match the following /
path url Whether open Matching results
/a /a/ true yes
/a /a/c true yes
/a /a true no

4, the Router

Low level routing, applicable to any routing component, is mainly deeply integrated with redux and must be used in conjunction with the History object

The purpose of using the Router route isto synchronize with the status management library such as history in Redux

<Router history= {history} >... </Router> Copy the codeCopy the code

5. Link and NavLink

Both routes are hops. NavLink has more parameters

The Link of the API

  • There are two ways to write to indicate which route to jump to
  • String writing

<Link to="/a"/> Copy the codeCopy the code

  • Object to write

<Link to={{
  pathname: '/courses',
  search: '? sort=name'.hash: '#the-hash',
  state: { fromDashboard: true}}}/> Copy the codeCopy the code

  • Replace is changing push to replace
  • InnerRef accesses the DOM of the Link tag

NavLink API

  • All of Link’s apis
  • ActiveClassName Class name set when the route is activated
  • ActiveStyle Style of route activation Settings
  • Exact reference Route. If this condition is met, the active class will be activated
  • Strict Refer to Route. The active class is activated only when this condition is met
  • IsActive receives a callback function that is fired when the active state changes. Returning false interrupts the jump

const oddEvent = (match, location) => {
  console.log(match,location)
  if(! match) {return false
  }
  console.log(match.id)
  return true
}
<NavLink isActive={oddEvent} to="/a/123"> component a </NavLink> copy codeCopy the code

  • Location receives a location object. The URL will jump only if it meets the conditions of this object

<NavLink to="/a/123" location={{ key:"mb5wu3", pathname:"/a/123"}}/> Copy the codeCopy the code

6, Redirect

Redirect Redirect is easy. We just look at the code

<Redirect to="/somewhere/else"/> <Redirect to={{pathname:"/login",
    search: "? utm=your+face"State: {referrer: currentLocation}}} /> <Redirect push to="/somewhere/else"/> <Redirect from= <Switch> <Redirect from= <Switch> <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path'Component ={Place}/> </Switch> Copy the codeCopy the code

7, the Switch

If you switch routes, only the first route is matched, so you can think of it as the TAB bar

The Switch contains only Route, Redirect, and Router

<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user"Component ={User}/> <Route Component ={NoMatch}/> </Switch> Copy codeCopy the code

8 withRouter.

When a non-routing component also wants to access the match,location, and history objects of the current route, then withRouter is a good choice. Think of it as wrapping a component as a routing component

import { withRouter } from 'react-router-dom'
const MyComponent = (props) => {
    const { match, location, history } = this.props
     return( <div>{props.location.pathname}</div> ) } const FirstTest = withRouter(MyComponent); Copy the codeCopy the code

9. History object

The React-Router has both component navigation and programmatic navigation. How do you use the React-Router API to control forward, backward, and refresh? Now we need to explain what the history object does

We can use this.props. History to get the history object from each routing component, or we can wrap the component around withRouter.

Push, replace, go and other methods are encapsulated in history. The specific contents are as follows

History { length: number; action: Action; location: Location; push(path: Path, state? : LocationState): void; Push (location: LocationDescriptorObject): void; push(location: LocationDescriptorObject): void; push(location: LocationDescriptorObject): void; Replace (path: path, state? : LocationState): void; GoBack Replace (location: LocationDescriptorObject): void; // Replace the current path with a page. Go (n: number): void; GoBack (): void; GoForward (): void; // Advance a page block(prompt? : boolean | string | TransitionPromptHook): UnregisterCallback; listen(listener: LocationListener): UnregisterCallback; createHref(location: LocationDescriptorObject): Href; } copy codeCopy the code

So if we want to use the API to move forward and back we can call the method in history

Secondly, it can also be achieved by secretly rotating the History library. The specific case is as follows

import { BrowserRouter } from 'react-router-dom';
const history = require('history').createBrowserHistory(); /** * forceRefresh: bool * Function: when the browser does not support HTML5historyAPI to force a page refresh. */ const supportsHistory ='pushState' in window.history;
 <BrowserRouter
        history= {history}
        basename="/"forceRefresh={! SupportsHistory} > {/* Route entry */}...... </BrowserRouter>Copy the code

10. Route authentication

Fourth, performance

After writing React for a while, I’ve grown to like writing apps using React.

We know that one of the things Facebook used when it launched React was performance.

Today, let’s talk about the performance optimization of React, and think about what other means can be used to improve the performance of React, so that our React faster and better performance.

1. React component performance optimization (render Angle optimization)

1. React performance viewing tool

Before we get into performance tuning, we need to look at the tool that allows us to see how long it takes to load components in React. Before React 16, we could use React Perf to do this.

Before the react16 release, we could use the React-Addons-Perf tool to view, but in the latest 16 release, we just need to add? React_pref.

Let’s start with react-Addons-Perf.

React-addons-perf is an official performance kit from React that prints out component rendering times, times, wasted time, and more.

Briefly say a few API, specific usage can refer to the official website:

  • Perf.start()Start recording
  • Perf.stop()The end of the record
  • Perf.printInclusive() View all designed components render
  • Perf.printWasted() View unneeded waste component Render

Install the React Perf extension in Chorme and add the corresponding code in the entry file or redux store.js:

Also, in the latest version of React16, add? React_pref, in chrome performance, we can look at User Timeing to see the component load time. Click “Record” to start recording. Note that the recording duration should not exceed 20 seconds, otherwise Chrome may be suspended.

You can see the following figure for the specific operation of using this tool:

2. Performance optimization of single React component

1. Minimize the number of new variables and bind functions in render, and minimize the number of passed parameters.

Let’s start by thinking about, for example, if I want to implement a button click that increases num by 1, what are the methods we have?

As you can probably imagine, there are no more than three, as shown below:

The first is to bind this to the constructor, the second is to bind this to the render() function, and the third is to use the arrow function.

But which method has the best performance is a question for us to consider. Everyone should know the answer: the first kind of performance is the best.

Because of the first, the constructor is only executed once per render;

And the second way, every timerender()The function is executed again when the function is executed;

The third way, every timerender()Generates a new arrow function even if the contents of the two arrow functions are the same.

For the third method, we can give an example. React determines whether render is a shallow comparison, which is simply determined by ===. If the type of state or prop is string or number, as long as the values are the same, the shallow comparison will consider them the same.

But if the former type is a complex object, we know that the object is a reference type, and shallow comparisons only consider these twopropIs it the same reference? If not, the two objects are considered distinct even if their contents are identicalprop.

Here’s an example:

When we assign a prop named style to the component Foo;

<Foo style={{ color:"red" }}Copy the code

With this method, each rendering is considered a style and the prop is changed because each time an object is generated for the style.

If you want React to render with a prop of the same type, you must make sure that the prop points to the same javascript object, as follows:

const fooStyle = { color: "red"}; <Foo style={fooStyle} /> <Foo style={fooStyle} />Copy the code

Custom shouldComponentUpdate function

ShouldComponentUpdate is a function that determines when a React component should not be rerendered, but the default implementation is simply to return true. That is, the default is to call all the lifecycle functions used, including the render function, for each update.

Let’s look at an example below

We write two components, App and Demo, and write two methods, one to change the num value in App, one to change the title, we print the render function in Demo. We can see the following effect:

We can clearly see that although the title value in the demo component has not changed, it is still render.

To solve this problem, we can modify the Demo component as follows:

Only when the title value of the demo changes, we go to render, we can see the effect:

This is just a very simple customization to shouldComponentUpdate.

In the latest react, React provides us with React.pureComponent. The official plugin called React-addons-Pure-Render-mixin was provided earlier to implement the shouldComponentUpdate lifecycle method.

The effect of using the above method is the same as our custom shouldComponentUpdate.

It should be noted, however, that PureRender here is a light comparison, because deep comparison scenes are quite expensive. So we need to pay attention to some of the things we talked about in 1.1: don’t set props or arrays directly, and don’t bind methods directly to elements, because functions are objects too

3. Optimize the performance and key of multiple React components

When the React component is loaded, react creates a tree structure in memory through the render method. The nodes in the tree represent a react component or native Dom element. This tree structure is called Vitural Dom.

React compares the original Vitural Dom and the newly generated Vitural Dom in the update stage, finds out the differences, and then renders the Dom tree according to the differences.

In order to achieve high performance, React uses the time complexity of O(N) to compare the difference between the two attribute structures, because the exact comparison between the two tree structures requires O(N^3), which degrades performance.

Simply put, React uses a key to identify components, which is an id badge, much like our ID cards are used to identify a person.

A list of key

  • The key is not passed to the component. If you need to use the value of the key, call it something else
  • When a list is reordered, it is not appropriate to use the index of the array as the key

* Note: There is another option: it is recommended to use shortid to generate an array of unique keys and use it with the data array, eliminating the need to reassemble the array when the data is submitted.

Case study:

import React from 'react';
import shortid from 'shortid';

class Demo extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      data: ['a'.'b'.'c']
    }
    this.dataKeys = this.state.data.map(v => shortid.generate());
  }
  
    deleteOne = index => { // 删除操作
        const { data } = this.state;
        this.setState({ data: data.filter((v, i) => i !== index) });
        this.dataKyes.splice(index, 1);
    }
    
    render() {
      return( <ul> { data.map((v, I) = > < li onClick = {I = > enclosing deleteOne (I)} key = {this. DataKeys [I]} > {n}} < / ul > < / li >))}} / / extraction, slightly can encapsulate a generic componentsCopy the code

It should also be noted that:

The key is not intended to improve react performance, but it does.

You cannot use random to use a key

React updates only component attributes if component attributes change. No changes, no updates.

React destroys the component (constructor and componentWillUnmount) and then recreates it (constructor and componentWillUnmount).

Let’s give you a few examples and you’ll understand right away:

  • Different node types

/ / A component < div > < Todos / > < / div > / / B component < span > < Todos / > < / span >Copy the code

We want to update component A to component B. When React made A comparison, it found that the outermost root node was different, so it directly abolished the previous

node, including the inner child node, which was A huge waste, but in order to avoid the time complexity of O(N^3), we could only use this way, so during the development process, We should try to avoid this and not change the type of the wrapped node arbitrarily.
  • The two nodes are of the same type

There are two cases where the node is a Dom type and there is a React component.

For dom types, let’s take an example:

// <div style={color: RGB (0, 0, 0);'red',fontSize:15}} className="welcome"> Hello World!!! <div style={color: # ff0000; color: # ff0000; color: # ff0000;'green',fontSize:15}} className="react">
  Good Bye!!!
</div>Copy the code

The difference between A and B components above is that the color in the text, className, and style changes, because the Dom element does not change, React only changes the part of it that changes.

For react component types, rendering is nothing more than going through the update process of the component instance. The most important thing is to customize shouldComponentUpdate, which we also mentioned above, so I won’t go into details.

  • Multiple child components

Let’s look at two examples

Example 1:

// A
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
  <TodoItem text="Third" complete={false} />
</ul>Copy the code

To change from A to B, if shouldComponentUpdate is handled properly, we only need to update the third load once.

Let’s look at the next example:

// A
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem text="Zero" complete={false} />
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>Copy the code

Since react adopts the time complexity of O(n), it will change the text for First to Zero, then change the text for Second to First, and then add a component at the end, text for Second. The properties of the two existing texts have been changed, so they are rendered in turn.

If we have 1000 instances here, then 1000 updates will happen.

That’s what we’re going to use hereKeythe

Basically, this Key is the REACT component id number.

If we change the previous example to the following, we can avoid the above problem. React can know that the second and third components in B are actually the first and second instances in A.

// A
<ul>
  <TodoItem key={1} text="First" complete={false} />
  <TodoItem key={2} text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem key={0} text="Zero" complete={false} />
  <TodoItem key={1} text="First" complete={false} />
  <TodoItem key={2} text="Second" complete={false} />
</ul>Copy the code

But now, React will remind us not to forget to use the key, and if we don’t use it, we’ll get an error in the browser.

aboutkeyIt is important to note that the key value should be stable, as inId numberIt’s the same thing that’s stable for us.

A common mistake is to use the subscript of an array as a key. This is dangerous and should be avoided.

<ul>
  {
        todos.map((item, index) => {
            <TodoItem
              key={index}
              text={item.text}
              completed={item.completed}
        })
  }
</ul>Copy the code

2. Redux performance optimization: RESELECT (data acquisition optimization)

MapStateToProps is also called selector, and it will be called whenever the store changes, whether or not the data that the selector is interested in changes, so if the selector computation is heavy, recalculating it with each update may cause performance problems.

Reselect can help you eliminate these unnecessary recalculations.

Reselect provides the createSelector function to create memorizable selectors.

The createSelector takes an array of input-selectors and a conversion function as parameters.

If a change in the state tree causes the input-selector value to change, the selector calls the conversion function with input-selectors and returns the result.

If the input-selectors have the same value as the previous one, they will return the data directly from the previous calculation without calling the conversion function again.

This avoids unnecessary computation and results in a performance boost.

Example:

import {} from 'reselect';
export const getItems = (state) => state.cart.get('items');
export const getItemsWithTotals = createSelector(
[ getItems ],
(item) => {
 return items.map(i =>{
  return i.set('total', i.get('price', 0) * i.get('quantity'); }); })Copy the code

Create a memorized selector. This means that the getItemWithTotals totals will be computed the first time the function is run.

If the same function is called again, but the input value (the value of getItems) does not change, the function will return a cached calculation.

If the items are modified (e.g., item added, quantity changed, anything that operates on the result of getItems), the function is executed again.

In the previous optimization process, we optimized the rendering to improve performance. Since React and Redux both drive the rendering process in a data-driven way, the process of optimizing the rendering process and obtaining data is also an optimization point that needs to be considered.

Const getVisibleTodos = (todos, filter) => {switch (filter) {case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      returntodos.filter(t => ! t.completed) } } const mapStateToProps = (state) => {return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}Copy the code

The mapStateToProps function is an important part of retrieving data from the Redux Store. When displaying to-do items based on the filter and todos, we need to walk through an array of toDOS fields.

When the array is large, it degrades performance.

This is where resELECT comes in. It works by using the last cached result as long as the relevant state has not changed.

Specific usage I will not introduce too much here, there have been a lot of cattle people wrote related articles, I will not repeat to write, if you want to understand, you can refer to resELECT giuhub, Redux middleware – ResELECT.

3. Separate code

1. Dynamic loading

The ES6 standard introduces import to make it easier for us to load modules statically. Forms such as:

import xxx from xxx.jsCopy the code

Although import is useful for us to load modules, the way we load modules statically limits us to asynchronous module loading to some extent. However, the import() syntax for dynamically loaded modules is currently in the proposal stage, and WebPack has already introduced it and used it. Import () provides a Promise-based API, so the return value of import() is a Promise object in complete or reject state. Forms such as:

import(/* webpackChunkName: 'module'* /"module")
    .then(() => {
        //todo
    })
    .catch(_ => console.log('It is an error'))Copy the code

At compile time, WebPack recognizes dynamically loaded import syntax, and webPack creates a separate bundle for the currently dynamically loaded module. If you’re using the official Create-React-app scaffolding or react’s server-side rendering framework nex.js, you can use the dynamic import syntax directly. If your scaffolding is webpack configured by yourself and you need to follow the official guidelines, go [1].

2. Dynamically load React components

One of the most popular methods is to use the React-loadable [2] library to lazily load the React component. It uses the import() syntax and the Promise syntax to load the React component. Also, React-loadable supports React server rendering. In general, we implement components as follows:

import LazyComponet from 'LazyComponent';
export default function DemoComponent() {
    return (
        <div>
            <p>demo component</p>
            <AComponent />
        </div>
    )
}Copy the code

In the example above, we assume that LazyComponet is not shown when the DemoComponent is rendered. But because we use the import syntax to import LazyComponet, we package the LazyComponet code into the same bundle as the DemoComponent code at compile time. But that’s not what we want. So we can use React-loadable to lazily load LazyComponet and package LazyComponet’s code into a bundle. Take a look at the example provided on the website:

import Loadable from 'react-loadable';
import Loading from './my-loading-component';
const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});
export default class App extends React.Component {
  render() {
    return<LoadableComponent/>; }}Copy the code

As we can see from the example, React-loadable uses the dynamic import() method and assigns imported components to the Loader property. Also, React-loadable provides a loading property to set the component to be displayed when the component is loaded.

Use of lazy and suspense

React.lazy and Suspense is not yet available for server-side rendering. If you want to do code-splitting in a server rendered app, we recommend Loadable Components. It has a nice guide for bundle splitting with server-side rendering.

Before using it, it’s important to note that React is officially supported, but React. Lazy and Suspense do not support server rendering. Therefore, for those who use server-side rendering, please detour to React-loadable and loadable components [3].

Since I am upgrading the original project, the following contents of this article mainly focus on the work I have done in upgrading the React latest version of the old project.

1. Upgrade the code of React latest version

If your code is Reactv16, you can upgrade directly to the latest version, though lazy and suspense methods are already available in React16.6. Before V16, migrate according to the official operation.

2. Identify lazy loaded components of the original code

First, according to the requirements, the components that are not loaded on the first screen are determined to be lazy loaded components. My project identified a total of five components that can be lazy loaded. The modification method is very simple. The original method of introducing components is:

import LazyComponent from ".. /components/LazyComponent ";Copy the code

Is amended as:

const LazyComponent = React.lazy(() =>
  import(/* webpackChunkName: 'lazyComponent'* /".. /components/LazyComponent"));Copy the code

Here’s the code: Replace the static reference component code with a call to React. Lazy (), passing an anonymous function as an argument to lazy(), and dynamically introducing lazyComponent into the function. This will prevent the browser from downloading the lazyComponent.bundle.js file and its dependencies before we render the component. WebpackChunkName in import is the bundle name we defined.

What happens when React tries to render a component but the component’s dependent code hasn’t been downloaded yet? Help us out with problems in
Before the code is downloaded, it will render the value passed in by the Fallback property. So our original code is:

return (
        <div>
            <MainComponet />
            <LazyComponent />
        </div>
    )Copy the code

Is amended as:

returnSuspense ={<div> </div> < Suspense> </div>!!Copy the code

Fallback can be changed to any spinner, this time do not do much optimization. React will give you errors if you don’t use it in suspense, so remember to use React. Lazy with React. At this point, we can see from the network request that the dynamically loaded lazyComponet component is individually packaged into a bundle. However, when the first screen is loaded, the bundle is already loaded into our page, which may not be what we want, we want to load when we need. Then we’ll take the control to load the file when we need it.

3. Control loading through variables

Originally, the five lazy loading components I chose are all of the components of the shell layer, so it is necessary to set a state to control the display and hiding of this component, so we change the code to:

return(< div > < MainComponet / > {this. State. ShowLazyComponent && (< React. The Suspense fallback = {< div > < / div >} in being loaded > < LazyComponent /> </React.Suspense> )} </div> )Copy the code

As a result, the bundle for our lazy-loaded LazyComponent is not loaded on the first screen load. The page does not load the JS until we click to display the component. This accomplishes our goal of code separation and lazy loading. So if we do this, does the size of the main bundle decrease? Next, let’s pack up the files and take a look.

4. Package your files

Files packaged before optimization:

The optimized package files:

The app.js file gets smaller, so add lazyComponent.js. When there are too many components to load lazily, we can reduce the size of the first screen load file to some extent and improve the first screen rendering speed. This experiment only extracts one component as an example, if the number of lazy loading is large, it is enough to significantly reduce the volume of app.js.

Summary — > Verify the effectiveness of optimization

5. [Verify optimization timeliness] Compare Puppeteer and Performance APIS

In order to verify the effectiveness of the previous optimization, I made a group of comparative experiments. In the experiment, puppeteer was used to access the pre-optimized and optimized pages 1000 times respectively, and the Performance API was used to calculate the average value of the five data items during the 1000 visits. The experimental results are shown in the figure below, where:

  • A indicates the average request time
  • B indicates the average time spent parsing the DOM tree
  • C is the average time between request completion and DOM loading
  • D is the average time between the start of a request and the end of domContentLoadedEvent
  • E indicates the average time between request start and load


The line chart cannot show the data accurately, so the attached table data is as follows (all are average time consuming) :

category The optimized Before optimization

A request (request)

7.01 7.04

B(parse the DOM tree average)

30.28 32.59

C(DOM loading after request)

552.86 582.0

D(request starts at end of domContentLoadedEvent)

569.13 589.0

E(request start to load end)

1055.59 1126.94

From the data, we can see that the request time before and after optimization has no impact, but the overall load time is significantly shortened and immediately enters the 1000ms mark, which shows that the first screen loading speed is significantly improved after optimization.

Note: While puppeteer is running 1000 times, the network fluctuates and some requests are large, so the average does not fully reflect the normal request time. But an average of 1000 times is enough to compare request times before and after optimization.

6. [Verify the timeliness of optimization] Use Chorme Performance parameters for comparison

Because the Performance API provides limited parameters, I got the parameters for a single page request from Chrome’s Performance summary. Since this is a single data set, we do not make detailed comparisons. It is listed here only to show which parts of the browser render time improved before and after optimization. Before optimization: After optimization:

  • Blue: The Loading time decreases
  • Yellow: Scripting time decreased
  • Purple: Rendering time reduced
  • Green: Painting time is flat
  • Gray: The Other time is reduced
  • Idle: The idle time of the browser decreases

In addition, I found from the Network that after optimization, the request time of the main interface was advanced because the page was parsed faster than before.

4, summarize

According to multiple data, use of React. Lazy and React.Suspense speeds up the first screen rendering to some extent, making our pages load faster. In addition, when we introduce a new dependency when we want to add a new feature, we tend to evaluate the size of the dependency and how much the introduction of the dependency will affect the original bundle. If the feature is rarely used, you can happily use React. Lazy and React.Suspense to load the feature on demand without sacrificing user experience.

Use react.memo () to optimize the performance of function components

Eact16.6 added another one specifically for optimization
Function component(Functional Component) Performance methods:
React.memo.


1. Useless rendering

A component is a basic unit that makes up the React view. Some components have their own local state, and when their value changes due to user action, the component is rerendered. In a React application, a component may be rendered frequently. While a few of these renderings are necessary, most of them are useless, and their presence can significantly degrade the performance of our application.

Look at this example:


import React from 'react';

class TestC extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')}render() {
        return( <div > {this.state.count} <button onClick={()=>this.setState({count: 1})}>Click Me</button> </div> ); }}export default TestC;

Copy the code

The TestC component has a local state, count, with an initial value of 0(state = {count: 0}). When we Click the Click Me button, the value of count is set to 1. The number on the screen will change from 0 to 1. When we click the button again, the count is still 1, and the TestC component should not be rerendered, but is it?

To test whether the component will be rerendered if count is repeatedly set to the same value, I added two lifecycle functions for the TestC component: componentWillUpdate and componentDidUpdate. The componentWillUpdate method is called when the component is about to be rerendered, and the componentDidUpdate method is called after the component has been successfully rerendered.

Run our code in a browser and Click the Click Me button several times. You can see the following output:



We can see that ‘componentWillUpdate’ and ‘componentWillUpdate’ are printed in the console every time we click the button. So even if the count is set to the same value, the TestC component will still be rerendered. This is called useless rendering.


2. Function components

We discussed how to use the PureComponent and shouldComponentUpdate methods to optimize the performance of class components. While class components are the main Component of a React application, Functional components can also be used as React components.

function TestC(props) {
    return (
        <div>
            I am a functional component
        </div>
    )
}
Copy the code

For function components, They don’t have anything like state to hold their local state (although function components can use useState to do so under React Hooks), so we can’t control the rerendering of function components like shouldComponentUpdate in class components. Of course, we can’t use the extends React.PureComponent because it’s not a class at all.

To explore a solution, let’s first verify that function components have the same problem with useless rendering as class components.

First let’s convert ES6’s TestC class to a function component:

import React from 'react';

const TestC = (props) => {
    console.log(`Rendering TestC :` props)
    return ( 
        <div>
            {props.count}
        </div>
    )
}
export default TestC;
// App.js
<TestC count={5} />

Copy the code

When the above code is first loaded, the console output is:



Again, we can open the Chrome debug tool, click the React TAB and select the TestC component:



We can see that the parameter value of this component is 5. Let’s change the value to 45, and the browser outputs this



The console outputs Object{count: 45}. Let’s set the count value to 45 again and look at the console output:

The output shows that the component has been rerendered even though the count value remains the same, 45.

Since function components also have the problem of useless rendering, how can we optimize them?

3. Solution: use react.memo ()

React.memo(…) This is a new attribute introduced in React V16.6. It acts like the React.pureComponent and controls rerendering of function components. React.memo(…) This is the function component’s React.PureComponent.

How to use React. Memo (…) ?

React. Memo is easy to use, assuming you have the following function components:

const Funcomponent = ()=> {
    return (
        <div>
            Hiya!! I am a Funtional component
        </div>
    )
}
Copy the code

All we need to do is pass the Funcomponent above as an argument to React. Memo:

const Funcomponent = ()=> {
    return (
        <div>
            Hiya!! I am a Funtional component
        </div>
    )
}
const MemodFuncComponent = React.memo(FunComponent)
Copy the code

Memo returns a Purified component, MemoFuncComponent, which will be rendered in JSX tags. When a component’s props and state change, React will check whether the former props and state are the same as the next one. If so, the component will not be rendered. If not, the component will be re-rendered.

Now let’s optimize the TestC component using React. Memo:

let TestC = (props) => {
    console.log('Rendering TestC :', props)
    return ( 
        <div>
        { props.count }
        </>
    )
}
TestC = React.memo(TestC);
Copy the code

Open the browser and reload our application. Then open the Chrome debugger, click the React TAB, and select the

component.
(testc)>

Then edit the props value and change the count to 89. We should see our application re-rendered:



Then repeat to set count to 89:



No rerender here!

This is the React. Memo (…). This function is awesome!

The one before us didn’t use React. Memo (…) In the example, the repeated setting of count causes the component to rerender. However, when we use React. Memo, the component will not be rerendered as long as the value passed in remains the same.

High order Component (HOC)

Higher-order functions, functions that can be passed functions as arguments, such as map,sort,reduce. A higher-order component is a component that wraps another component.

  1. Props Proxy
  2. Inheritance Inversion

Immutable.js

Because the data is immutable, you can avoid the problem of passing values by reference, but it’s also troublesome

Stateless component

Using stateless components to receive props only from the parent can improve the rendering performance of the component

const HelloWorld = (props) => <div>{props.name}</div>ReactDOM.render(<HelloWorld name="HelloWorld" />,App)Copy the code

The value of componentWillReceiveProps of props

NextProps, not this.props

Bind function

By default, the bind function has an event parameter, but this parameter is after the given parameter

handleClockClick (id, e) {console.log(id,e)}<button onClick={this.handleClockClick.bind(this, 2)}>Clock</button>Copy the code

In the ES6 class, the function this does not point to an object by default

Access to elements

  • This.getdomnode was removed in earlier versions, and now set ref= XXX, then use this.refs.xxx to access the DOM element
  • Ref can be assigned to either a string or a function. Strings can only be assigned to class components, functions to DOM elements, and refs cannot be assigned to pure function components. Old versions of DOM elements can use ref, but React is not recommended.

ref="test"// this.refs.test access ref={test => this.test = test} // this. Test accessCopy the code

Composition VS Inheritance

React recommends using composition instead of inheritance. Composition is more intuitive in the UI, and the code looks easier. It is more in line with our understanding and the React Component-base feature.

The default value is true when only the property name is written

<MyComponent isStock/>// isStock defaulttrueCopy the code

createPortal

  • The method to mount the child node to a component other than the parent, used in the render method, cannot be mounted to the parent component because the parent component is not rendered and cannot get the DOM
  • The React DOM tree is still on this component, including event bubbles and things like that
import { createPortal } from 'react-dom'createPortal(this.props.children, document.getElementById('portal-root'))Copy the code

Error boundary

ComponentDidCatch Error bounds that define a parent component for the component that catches the error and provides the UI to fall back on

  • usage
componentDidCatch(error, info) {this.setState({ hasError: true}); console.log(error, info)}Copy the code
  • Error events that cannot be caught handle asynchronous code (such as setTimeout or requestAnimationFrame callbacks) the server side renders errors thrown by the error boundary itself (rather than its children)

High-level components are functions

  • You should not modify the properties of the original component in a higher-order component
  • Wrap a component with a function, returning a new component
  • Switch different data sources for the component
  1. Showcase component, using the getData (Showcase, data) function package, obtain different data
  • Do not use higher-order components in Render because each time a component is mounted, an instance of the higher-order component is retrieved
  • hoistNonReactStatic Copy the static methods of the original component into the wrapped component

Container components

  • Handles data subscription and status management
  • Higher-order components are parameterized container components

rander prop

The title is the same, and the higher-order components are used to render the title to different components

Use the Web Component in React

One thing to note at this point is that you should use class, not className, for the Web Component

The lable for

For is a reserved word in JS, so use htmlFor instead

Style property

Browser suffixes, with the exception of MS, should begin with a capital letter. That’s why WebkitTransition has a capital W.

const divStyle = {WebkitTransition: 'all', // note the capital 'W' heremsTransition: 'all' // 'ms' is the only lowercase vendor prefix};Copy the code

Use React16 below IE11

React16 relies on the collection types Map and Set, and requires a polyfill, such as core-js and Babel-Polyfill, in browsers that do not provide native support

Use core-JS support

import 'core-js/es6/map'; import'core-js/es6/set'; import React from'react'; import ReactDOM from'react-dom'; ReactDOM.render(<h1>Hello, world! </h1>,document.getElementById('root'));Copy the code

ComponentDidMount Requests server data

When componentDidMount requests server data and uses setState, note that componentWillUnmount should be removed

Pass component props using the new ES6 feature

const {data, type} = this.state; // Demo data={data}type= {type}/> // es6 method <Demo {... {data,type}} / >Copy the code

3. Use ES6 REST parameters (in the form… Passes a variable number of props

Const Demo = ({prop1, prop2,... RestProps}) => (<div>{restProps. Text}</div>) // Demo < prop1={XXX} prop2={XXX} text={XXX}/>Copy the code

Other uses of setState

Const {data} = this.state; this.setState({ data: {... data, key: 1 } }); This.setstate (({data}) => ({data: {... data, key: 1 } })); This.setstate ((state, props) => {return { counter: state.counter + props.step };
});Copy the code

5. React performance optimization

// React performance optimization can be done in a variety of ways. One of the common ways is to use the life cycle function shouldComponentUpdate to determine certain values or properties that control whether a component is rerender. // It's easier to handle strings, numbers, or basic objects or arrays // It's harder to handle nested objects or arrays. For this // it is recommended to use the isEqual of lodash(or any similar library) to determine nested arrays or objects shouldComponentUpdate(nextProps, nextState) {if (_.isEqual(nextState.columns, this.state.columns)) return false;
return true;
}Copy the code

React Advanced – Tips (28 videos) link

Introduce some advanced knowledge points of React, some practical skills of development, some tool libraries, etc.

The video can be updated at www.qiuzhi99.com/

React Advanced – Tips

React Tips #1 How to deploy a React app with netlify Cloud Service

React #2 Deploy the React app to GitHub Pages 18 “05:34”

#3 React-Router Tutorial Part 1 51 “10:29”

#4 React-Router Tutorial Part 2 11 “07:39”

React Advanced #5 best way to write stateless components

#6 Fragment 14 “Pro” “02:36”

React Advanced #7 Context 9 “03:58”

React Advanced Upgrade #8 advanced Component 14 “Pro” “02:51”

React Advanced #9 Powerful, cool and fun Web IDE (mouse click to generate code, reduce development time by N times)

React Advanced #10 Refactoring code with higher-order components

1 “Pro” “04:30” 1 “Pro” “04:30”

React Advancements #12 Correct way to return multiple components

React Advanced upgrade #13 Netlifyctl deploy front-end application 2 “06:49”

React Advance raise #14 defaultProps and type check PropTypes part 14 “06:37”

PropTypes Part 2 “Pro”

#16 React Props instead of HOC

React Advanced #17 Error boundary and lifecycle function componentDidCatch 9 “Pro” “11:45”

React Upgrade #18 to 16.3 “02:37”

9 “03:50”

React 16.3 new Context API 1 “06:50”

#21 React 16.3 New Context API – Practice 3 “Pro” “09:19”

Change the Redux Context API to the React 16.3 Context API

React Advanced #23 Objects, arrays, mutable data 9 “Pro”

#24 React.Children API and props. Children 解 视 4 “Pro” “06:06”

#25. Styled – Components 5 “Pro” “04:56”

#26. Styled – Components (Practice) “Pro”

You should use redux-form 12 “Pro” “06:40”

Redux-form (Practice) 7 “Pro”

Learning materials

data

Doc.react-china.org/ translated official documents, you must read the documents several times to learn technology

React little book strongly recommend, from the shallow to the deep, step by step

Reactpatterns.com/ Because the React API itself is relatively simple, close to native. A series of patterns are generated through component changes

Github.com/CompuIves/c… React Online editor, easy to share your React projects

devhints.io/react

Js. Coach find js package website

video

Basic free, premium charged egghead. IO