React introduce React and describe its features

1.1 introduction

React is a JavaScript library for building user interfaces and is a UI-level solution. React follows component design patterns, declarative programming paradigms, and functional programming concepts to make front-end application development more efficient. React, meanwhile, uses the virtual DOM to efficiently manipulate the DOM, following a one-way flow of data from higher-order components to lower-order components. React also helps us break down the interface into separate pieces. Each piece is a component. These components can be combined and nested to form a whole page.

Syntactically, the React class component uses a method or function called render() called return, which receives input data and returns what needs to be displayed, such as:

class HelloMessage extends React.Component {
  render() {
    return (
      <div>
        Hello {this.props.name}
      </div>
    );
  }
}

ReactDOM.render(
  <HelloMessage name="Taylor" />,
  document.getElementById('hello-example')
);
Copy the code

This XML-like form is JSX, which is eventually called by Babel as a legitimate JS statement. The data passed in is accessible in the component via this.props in Render ().

1.2 features

React has many features. Here are some features:

  • JSX grammar
  • One-way data binding
  • Virtual DOM
  • Declarative programming
  • Component

1.2.1 Declarative programming

Declarative programming is a programming paradigm that focuses on what you do, not how you do it. It expresses logic without explicitly defining steps. This means that we need to declare the components to be displayed according to a logical calculation, such as implementing a tagged map: the steps for imperative map creation, tag creation, and tag addition to the map are as follows.

Const map = new map.map (document.getelementById ('map'), {zoom: 4, center: {lat, LNG}}); Const marker = new map. marker({position: {lat, LNG}, title: 'Hello marker '}); // Add marker marker to map. SetMap (map);Copy the code

React implements the function as follows:

<Map zoom={4} center={lat, lng}>
    <Marker position={lat, lng} title={'Hello Marker'}/>
</Map>
Copy the code

Declarative programming makes the React component easy to use and the resulting code simpler and easier to maintain.

1.2.2 Component

In React, everything is a component. It is common to decompose the entire logic of an application into small, single parts. We call each individual part a component. A component can be a function or a class that takes data input, processes it, and returns the React element rendered in the UI. Functional components look like this:

const Header = () => {
    return(
        <Jumbotron style={{backgroundColor:'orange'}}>
            <h1>TODO App</h1>
        </Jumbotron>
    )
}
Copy the code

Stateful components are defined as follows:

class Dashboard extends React.Component { constructor(props){ super(props); this.state = { } } render() { return ( <div className="dashboard"> <ToDoForm /> <ToDolist /> </div> ); }}Copy the code

The React component has the following features:

  • Composable: Components are easy to use with other components or nested within another component.
  • Reusable: Each component is functionally independent and can be used in multiple UI scenarios.
  • Maintainable: Each small component contains only its own logic and is easier to understand and maintain.

Real DOM and Virtual DOM

2.1 Real DOM

Real DOM is relative to Virtual DOM. Real DOM refers to the document object model, which is an abstraction of structured text. Every node rendered on the page is a Real DOM structure, which can be viewed using the DevTool of the browser as follows.The Virtual Dom is essentially a description of the Dom in the form of JavaScript objects. The purpose of creating the virtual DOM is to better render virtual nodes into the page view. The nodes of the virtual DOM object correspond to the properties of the real DOM one by one. React JSX is a feature that allows you to declare the DOM structure of the interface directly in JS using XML.

Const vDom = <h1>Hello World</h1> Const root = document.getelementById ('root') // find <div id="root"></div> reactdom.render (vDom, Root) // Render the h1 tag to the root nodeCopy the code

In the code above, reactdom.render () is used to insert the created virtual DOM node into a real node and render it to the page. In fact, JSX is a syntactic sugar, which is compiled by Babel and converted into JS code, as shown in the VDOM.

Const vDom = react. createElement('h1', {className: 'hClass', id: 'hId'}, 'hello world')Copy the code

As you can see, JSX is designed to simplify calling the react.createElement () method directly:

  • The first parameter is the tag name, such as H1, SPAN, table… .
  • The second argument is an object that holds some attributes of the tag, such as ID, class, and so on.
  • The third parameter is the text in the node.

Using console.log(VDOM), you can obtain information about the virtual DOM.

So, as you can see from the above example, JSX is converted to a react. createElement execution via Babel, which returns an object, the virtual DOM. ,

2.2 the difference between

The differences between the Real DOM and Virtual DOM are as follows:

  • The virtual DOM does not typeset or redraw, whereas the real DOM rearranges and redraws frequently.
  • The total loss of the virtual DOM is “virtual DOM addition, deletion, and modification + real DOM difference addition, deletion, and modification + typesetting and redrawing”, while the total loss of the real DOM is “real DOM complete addition, deletion, and modification + typesetting and redrawing”.

2.3 the advantages and disadvantages

Advantages of the real DOM:

  • Easy to use

Disadvantages:

  • Low efficiency, slow parsing speed, high memory usage.
  • Poor performance: Frequent manipulation of the real DOM can easily lead to redrawing and backflow.

The advantages of using the virtual DOM are as follows:

  • Simple and easy: If you manually manipulate the real DOM to complete the page, it is tedious and error-prone, and difficult to maintain in large-scale applications.
  • Good performance: Using Virtual DOM can effectively avoid frequent updates of the real DOM number, reduce redrawing and backflow caused by multiple times, and improve performance.

– Cross-platform: React leverages the virtual DOM to bring cross-platform capabilities, enabling a set of code to run multiple times.

Disadvantages are as follows:

  • In some applications with high performance requirements, the virtual DOM cannot be optimized to the extreme.
  • Rendering a large number of DOM for the first time was slightly slower than normal due to the extra layer of virtual DOM computation.

C. What is the difference between super() and super(props)

3.1 ES6 class

In ES6, class inheritance is implemented using the extends keyword as follows:

class sup { constructor(name) { this.name = name } printName() { console.log(this.name) } } class sub extends sup{ Constructor (name,age) {this.age = age} printAge() {console.log(this.age)}} let jack = new sub('jack',20) jack.printname () // output: jack jack.printage () // output: 20Copy the code

In the example above, you can see through the super keyword to call the superclass, super substitute are building a function of the parent class using super (name) is equivalent to calling sup. Prototype. The constructor. Call (this, name). If the super keyword is not used in a subclass, an error is raised as follows:The reason for the error is that a subclass does not have its own This object. It can only inherit this from its parent class and process it. Super (), on the other hand, inherits this from its parent class to subclasses. Without super() subclasses, you can’t get this. If you call this first and then initialize super(), this is also prohibited.

Class sub extends sup{constructor(name,age) {this.age = age super(name) // super represents a constructor}}Copy the code

Therefore, in subclass constructor, you must first use super to refer to this.

3.2 class components

In React, the class component is implemented based on the ES6 specification and inherits from react.component, so if you use constructor you must write super() to initialize this. In this case, when calling super(), we usually need to pass props as the parameter. Otherwise, React will define it in the component instance.

// React inner const instance = new YourComponent(props); instance.props = props;Copy the code

This. Props can be used in render with or without constructor. React is automatically supplied with this.

class HelloMessage extends React.Component{ render (){ return ( <div>nice to meet you! {this.props.name}</div> ); }}Copy the code

But using super() instead of super(props) is also not recommended. Because React assigns this. Props after the class component constructor generates an instance, calling this. Props returns undefined without passing props as super.

class Button extends React.Component { constructor(props) { super(); // No props console.log(props) is passed; // {} console.log(this.props); // undefined // ... }Copy the code

The functions passed to the props are accessible, ensuring that this. Props is assigned before the constructor completes execution.

class Button extends React.Component { constructor(props) { super(props); // No props console.log(props) is passed; // {} console.log(this.props); / / {} / /... }Copy the code

From the above example, we can conclude that:

  • In React, the class component is based on ES6, so super must be used in Constructor.
  • During the call to super, the React internal porps is assigned to the component instance porps property, regardless of whether it is passed to props or not.
  • If only super() is called, this.props will still be undefined between super() and the end of the constructor.

Talk about setState execution mechanism

4.1 What is the setState mechanism

In React, the display of a component can be determined by the data state and external parameters. The data state is state. When you need to change the state of the value inside, you need to call setState to change, so as to achieve the function of updating the data inside the component.

For example, here’s an example:

import React, { Component } from 'react' export default class App extends Component { constructor(props) { super(props);  this.state = { message: "Hello World" } } render() { return ( <div> <h2>{this.state.message}</h2> <button onClick={e => ChangeText () {this.setState({message: "JS "})}}Copy the code

Trigger the onclick event by clicking the button, execute the this.setState method to update the state, and then re-execute the Render function, causing the view of the page to update. If you want to change the state of state directly, you simply call setState.

ChangeText () {this.state.message = "Hello, world "; }Copy the code

We’ll see that the page doesn’t react, but the state of the state has changed. This is because React doesn’t listen for changes in data like vue2 when it calls Object.defineProperty or Vue3 when it calls Proxy. You must use the setState method to tell the React component that the state has changed. The definition of the state method is inherited from react.component. the source code for the definition is as follows:

Component.prototype.setState = function(partialState, callback) { invariant( typeof partialState === 'object' || typeof partialState === 'function' || partialState == null, 'setState(...) : takes an object of state variables to update or a ' + 'function which returns an object of state variables.', ); this.updater.enqueueSetState(this, partialState, callback, 'setState'); };Copy the code

4.2 Update Mode

When using setState to update data, the update types of setState are divided into asynchronous update and synchronous update.

4.2.1 Asynchronous Update

For example, here is the following code.

ChangeText () {this.setState({message: "hello"}) console.log(this.state.message); // Hello World }Copy the code

The code above prints Hello World and does not get the latest state immediately after executing setState. If you want to get the updated value immediately, the update will be executed in the callback for the second parameter.

ChangeText () {this.setState({message: "hello"}, () => {console.log(this.state.message); // Hello}); }Copy the code

4.2.2 Synchronizing Updates

Here is an example of synchronizing updates using setTimeout.

ChangeText () {setTimeout(() => {this.setState({message: "Hello}); console.log(this.state.message); }, 0); }Copy the code

4.2.3 Batch Update

Sometimes we need to deal with batch updates. Here is an example:

handleClick = () => {
    this.setState({
        count: this.state.count + 1,
    })
    console.log(this.state.count) // 1

    this.setState({
        count: this.state.count + 1,
    })
    console.log(this.state.count) // 1

    this.setState({
        count: this.state.count + 1,
    })
    console.log(this.state.count) // 1
}
Copy the code

When we click the button to trigger the event, we print all 1s, and the page shows the value of count as 2. If setState is executed for the same value multiple times, the batch update policy of setState will overwrite it and take the result of the last execution. Therefore, the above code is equivalent to the following code:

Object.assign(  previousState,  {index: state.count+ 1},  {index: state.count+ 1},  ...)
Copy the code

Because later data overwrites previous changes, it is eventually added only once. If the next state depends on the previous state, it is recommended to pass a function to setState as follows:

onClick = () => { this.setState((prevState, props) => { return {count: prevState.count + 1}; }); this.setState((prevState, props) => { return {count: prevState.count + 1}; }); }Copy the code

In setTimeout or native DOM events, there is no overwriting because the operation is synchronous.

React event binding

5.1 Event Binding

When we need to handle a click event, we need to add some binding to the event, called event binding. Here is one of the most common event bindings:

class ShowAlert extends React.Component { showAlert() { console.log("Hi"); } render() { return <button onClick={this.showAlert}>show</button>; }}Copy the code

As you can see, the event-bound methods need to be wrapped with {}. The above code looks fine, but when you change the handler output code to console.log(this) and click the button, you’ll see that the console output is undefined.

5.2 Common Binding Modes

The React event binding methods are as follows:

  • Bind is used in the render method
  • The arrow function is used in the Render method
  • In the constructor to bind
  • The definition stage uses arrow function bindings

5.2.1 Use bind in the render method

If you use a class component in which you give a component/element an onClick attribute, it will now and automatically bind its this to the current component.

class App extends React.Component {
  handleClick() {
    console.log('this > ', this);
  }
  render() {
    return (
      <div onClick={this.handleClick.bind(this)}>test</div>
    )
  }
}
Copy the code

In this way, every time the component render, it will re-bind the operation, affecting the performance.

5.2.2 Arrow function used in render method

Binding the reference to this to the current component via ES6 context also generates new methods every time render is performed, affecting performance.

class App extends React.Component {
  handleClick() {
    console.log('this > ', this);
  }
  render() {
    return (
      <div onClick={e => this.handleClick(e)}>test</div>
    )
  }
}
Copy the code

5.2.3 requires the bind in the constructor

Pre-bind the current component in Constructor to avoid repeated binding in the Render operation.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log('this > ', this);
  }
  render() {
    return (
      <div onClick={this.handleClick}>test</div>
    )
  }
}
Copy the code

5.2.4 Define phase using arrow function binding

As with the third method above, it avoids repeated binding in the Render operation and is very simple to implement.

class App extends React.Component {
  constructor(props) {
    super(props);
  }
  handleClick = () => {
    console.log('this > ', this);
  }
  render() {
    return (
      <div onClick={this.handleClick}>test</div>
    )
  }
}
Copy the code

5.3 the difference between

The differences of the above four methods are as follows:

  • Writing aspect: way one, way two writing method is simple, way three writing is too miscellaneous
  • Performance: Methods 1 and 2 generate a new method instance every time the component renders, which is lacking in performance issues. If this function is passed as an attribute value to a child component, it will result in additional rendering. Methods three and four generate a method instance.

In conclusion, mode 4 is the optimal event binding mode.

React communicates with components

6.1 Component Communication

Component is the core basic idea of Vue and React front-end frameworks, as well as one of the most obvious characteristics that distinguishes other JS frameworks. Typically, a completed complex business page is made up of many basic components. Communication is involved when components need to pass messages between them. Communication means that the sender transmits information to the receiver in a certain format through a certain medium to achieve a certain purpose. In a broad sense, the traffic of any information is communication.

6.2 Several modes of communication

Components can be delivered in a variety of ways, which can be classified according to the sender and receiver as follows:

  • Parent component passes to child component
  • Child component passes to parent component
  • Communication between sibling components
  • Parent components pass to descendant components
  • Non-relational component delivery

6.2.1 A parent component passes messages to a child component

Because React’s data flow is one-way, parent to child is the most common way. When a parent component calls a child component, it only needs to pass parameters in the child component tag. The child component can receive the parameters from the parent component through the props property.

function EmailInput(props) {
  return (
    <label>
      Email: <input value={props.email} />
    </label>
  );
}

const element = <EmailInput email="[email protected]" />;
Copy the code

6.2.2 Child components pass messages to parent components

The basic idea of a child communicating with its parent is that the parent passes a function to the child and then gets the value passed by the child through the function’s callback. The parent component corresponds to the following code:

class Parents extends Component { constructor() { super(); this.state = { price: 0 }; } getItemPrice(e) { this.setState({ price: e }); } render() { return ( <div> <div>price: {this.state.price}</div> {/* Pass a function to the Child component */} <Child getPrice={this.getitemprice.bind (this)} /> </div>); }}Copy the code

The code for the sub-component is as follows:

Class Child extends Component {clickGoods(e) {// Pass the value this.props. GetPrice (e); } render() { return ( <div> <button onClick={this.clickGoods.bind(this, 100)}>goods1</button> <button onClick={this.clickGoods.bind(this, 1000)}>goods2</button> </div> ); }}Copy the code

6.2.3 Communication between Sibling Components

If it is passed between sibling components, the parent component acts as an intermediate layer to realize data communication by using the parent component to pass.

class Parent extends React.Component { constructor(props) { super(props) this.state = {count: 0} } setCount = () => { this.setState({count: this.state.count + 1}) } render() { return ( <div> <SiblingA count={this.state.count} /> <SiblingB onClick={this.setCount} /> </div> ); }}Copy the code

6.2.4 Message Transmission in generational Groups

It is common for a parent component to pass data to a descendant component, just like global data. Using context provides a way for components to communicate with each other, sharing data, and allowing other data to read the corresponding data. Create a context by using React. CreateContext.

 const PriceContext = React.createContext('price')
Copy the code

After the context is created, the Provider component is used to create data sources, and the Consumer component is used to receive data. The example is as follows: The Provider component uses the value attribute to transmit data to future components.

<PriceContext.Provider value={100}>
</PriceContext.Provider>
Copy the code

To retrieve data passed by the Provider, it can be received by the Consumer component or using the contextType property as follows:

class MyClass extends React.Component { static contextType = PriceContext; render() { let price = this.context; /* Render work based on this value */}}Copy the code

The code for the Consumer component is as follows:

Price => <div>price: {price}</div>}</ pricecontext.consumer >Copy the code

6.2.5 Non-relational components pass messages

If the relationship type between components is complex, it is recommended to implement a global resource management of data to achieve communication, such as REdux, MOBx, etc.

Seven, the React of Hooks

7.1 Hook

Hook is a new feature in React 16.8. It lets you use state and other React features without having to write a class. As for why hook was introduced, the official motivation was to solve the problems frequently encountered during long-time use and maintenance of React, such as:

  • It is difficult to reuse and share state-related logic in components
  • Components with complex logic are difficult to develop and maintain. When our component needs to deal with multiple unrelated local states, each lifecycle function may contain a variety of unrelated logic
  • The this in the class component increases the learning cost, and the class component has some problems optimizing based on existing tools
  • Due to business changes, functional components have to be class components, and so on

Function components, also known as stateless components, initially do only some of the rendering. Therefore, using Hook technology, the function component can also be a stateful component, which can maintain its own state and do some logical processing internally.

7.2 Hooks functions

Hooks let our function component have the features of a class component, such as state within the component, life cycle, etc. Hooks provide a number of useful Hooks functions for state management, including:

  • useState
  • useEffect
  • other

useState

Here is an example:

import React, { useState } from 'react'; Const [count, setCount] = useState(0); function Example() {const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p > <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }Copy the code

In the function component through useState function internal maintenance of state, the argument is the default value of state, the return value is an array, the first value is the current state, the second value is the function to update state. If this function component is implemented as a class component, the code is as follows:

class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p > <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); }}Copy the code

From the above two code analyses, you can see the difference between the two:

  • State declaration: Gets it directly from useState in the function component and sets it in the constructor constructor for the class component
  • Read state: Use variables directly in function components, and get class components by this.state.count
  • State updates: setCount updates in function components and this.setstate () updates in class components

In general, useState is more concise to use, reducing the ambiguity of this.

useEffect

UseEffect allows us to do things with side effects in function components. For example, here is an example of a timer:

class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div> <p>You clicked {this.state.count} times</p > <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); }}Copy the code

As you can see above, the component does the same thing during both the load and update phases. If you useEffect, you can pull out the same logic in a way that a class component does not.

import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState(0);
 
  useEffect(() => {    document.title = `You clicked ${count} times`;  });
  return (
    <div>
      <p>You clicked {count} times</p >
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
Copy the code

UseEffect’s first argument takes a callback function. By default, useEffect is executed after the first rendering and update, which is equivalent to a callback to the componentDidMount and componentDidUpdate lifecycle functions.

If certain values do not change between rerenders, you can skip the effect call by passing in the second argument, as follows:

useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Update only when count changesCopy the code

After passing in the second argument above, if count is 5 and our component rerenders with count still equal to 5, React will compare the last render [5] to the last render [5] and skip effects if they are equal.

The callback can return a cleanup function, which is an optional cleanup mechanism, equivalent to the componentwillUnmount life cycle function in the class component, and can do some cleanup operations, as follows:

useEffect(() => {
    function handleStatusChange(status) {
        setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
        ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
});
Copy the code

It can be found that useEffect is equivalent to the combination of three life cycle functions componentDidMount, componentDidUpdate and componentWillUnmount.

Other hooks

React has many additional hooks in addition to the two more common ones above.

  • useReducer
  • useCallback
  • useMemo
  • useRef

7.3 summarize

With a look at the above, hooks make it easier to solve the problem of state-related reuse:

  • UseHook generates a separate state each time it is called
  • Custom hooks can better encapsulate our functionality

Write hooks for functional programming, each function is wrapped in a function, the overall style is more clean and elegant.

Talk about your understanding of Redux

8.1 concept

React is used to build the user interface to help with the DOM rendering process. In the whole application, there are many components, and the state of each component is managed by itself, including components defining their own state, communication between components passing through props, and data sharing using Context.

If each component stores its own associated state, theoretically it will not affect the operation of the application, but during development and subsequent maintenance, we will spend a lot of effort to query the state change process. In this case, if all the state is centrally managed, when the state needs to be updated, only the state needs to be centrally managed, without worrying about how the state is distributed internally to each component.

Redux implements centralized management of state and follows three basic principles when using it:

  • Single data source
  • State is read-only
  • Use pure functions to perform modifications

It should be noted that Redux is not only used in React, but also works with other interface libraries such as Vue.

8.2 Working Principle

Redux status management is divided into three parts: Action Creactor, Store and Reducer. Store is the common storage space for data. If one component changes the data content in the store, other components can perceive the changes in the store and fetch the data, thus indirectly realizing the function of data transmission.

The schematic diagram of the workflow is shown below.

For more information, see: Redux’s three core concepts

8.3 the use of

First, you need to create a common data area for the store.

Import {createStore} from 'redux' // Introduce a third party method const store = createStore() // Create a common storage area for data (administrator)Copy the code

Then, a log book is created to assist in managing the data, called a reduecer, which is essentially a function that takes two arguments, state and action, and returns state.

// Set the default value const initialState = {counter: 0} const reducer = (state = initialState, action) => {}Copy the code

Next, use the createStore function to connect the state and action as follows.

const store = createStore(reducer)
Copy the code

If you want to get the data in store, get the current state with store.getState(), as shown below.

console.log(store.getState());
Copy the code

Let’s take a look at how to change data in a store. Dispatch an action via Dispatch, which usually has a Type attribute and can also carry other data.

store.dispatch({ type: "INCREMENT" }) store.dispath({ type: "DECREMENT" }) store.dispatch({ type: "ADD_NUMBER", number: 5})Copy the code

Next, let’s look at modifying the processing logic in the Reducer.

const reducer = (state = initialState, action) => { switch (action.type) { case "INCREMENT": return {... state, counter: state.counter + 1}; case "DECREMENT": return {... state, counter: state.counter - 1}; case "ADD_NUMBER": return {... state, counter: state.counter + action.number} default: return state; }}Copy the code

Note that reducer is a pure function and there is no need to modify state directly. Then, when an action is issued, we can listen for changes in the store with store.subscribe.

store.subscribe(() => {
  console.log(store.getState());
})
Copy the code

In the React project, it is used with react-redux.

const redux = require('redux'); const initialState = { counter: > {switch (action.type) {case "INCREMENT": return {... state, counter: state.counter + 1}; case "DECREMENT": return {... state, counter: state.counter - 1}; case "ADD_NUMBER": return {... state, counter: state.counter + action.number} default: return state; Const store = redux.createstore (reducer); store.subscribe(() => { console.log(store.getState()); }) // Modify state store.dispatch({type: "INCREMENT"}) // console.log(store.getState()); store.dispatch({ type: "DECREMENT" }) // console.log(store.getState()); store.dispatch({ type: "ADD_NUMBER", number: 5 }) // console.log(store.getState());Copy the code
  • CreateStore helps create a store.
  • Store. dispatch helps dispatch actions, which are passed to the store.
  • The store. GetState method is used to retrieve all of the data in the store.
  • The Store. subscrible method subscribes to store changes, and the callback received by the store.subscrible function is executed whenever the store changes.

Redux middleware

9.1 What is Middleware

Middleware is a kind of software between application system and system software. It uses basic services (functions) provided by system software to connect various parts of application system or different applications on the network, so as to achieve the purpose of resource sharing and function sharing.

Previously, we learned about the whole work flow of Redux. When the action was sent, the Reducer immediately calculated the state, and the whole process was a synchronous operation. If you need to support asynchronous operations, or error handling, or log monitoring, middleware can be used for this process.

In Redux, the middleware is placed in the dispatch process and the action is distributed for interception, as shown in the following figure:It is essentially a function that reworks the store.dispatch method by adding additional capabilities between the issuing of actions and the Reducer step.

9.2 Common Middleware

There are many excellent Redux middleware, such as:

  • Redux-thunk: for asynchronous operations
  • Redux-logger: used for logging

All of the above middleware needs to be registered with applyMiddlewares, which is an array of all middleware that is executed in turn and passed into the createStore as a second argument.

const store = createStore(
  reducer,
  applyMiddleware(thunk, logger)
);
Copy the code

9.2.1 redux – thunk

Redux-thunk is a recommended asynchronous processing middleware. Dispatch (Action) by default, the action needs to be a JavaScript object.

The Redux-Thunk middleware will determine the type of data you are currently passing in and, if it is a function, will pass in parameter values (dispatch, getState) to the function.

  • The dispatch function is used to dispatch the action again later.
  • The getState function allows us to retrieve some of the previous states, allowing us to rely on them for future operations.

Therefore, dispatch can be written as the following function.

const getHomeMultidataAction = () => { return (dispatch) => { axios.get("http://xxx.xx.xx.xx/test").then(res => { const data = res.data.data; dispatch(changeBannersAction(data.banner.list)); dispatch(changeRecommendsAction(data.recommend.list)); }}})Copy the code

9.2.2 redux – logger

If you want to implement a logging feature, you can use an off-the-shelf Redux-Logger, as follows.

import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);
Copy the code

9.3 Redux source code analysis

First, let’s take a look at applyMiddlewares source code:

export default function applyMiddleware(... middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer); var dispatch = store.dispatch; var chain = []; var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(... chain)(store.dispatch); return {... store, dispatch} } }Copy the code

As you can see, all middleware is put into an array chain, nested execution, and finally store.dispatch, while the middleware (middlewareAPI) gets the getState and Dispatch methods

From the above analysis, we learned the basic use of Redux-Thunk. At the same time, internal dispatch will make a judgment, and then perform the corresponding operation, the principle is as follows:

function patchThunk(store) {
    let next = store.dispatch;

    function dispatchAndThunk(action) {
        if (typeof action === "function") {
            action(store.dispatch, store.getState);
        } else {
            next(action);
        }
    }

    store.dispatch = dispatchAndThunk;
}
Copy the code

Now, we implement a log output interception ourselves.

let next = store.dispatch; function dispatchAndLog(action) { console.log("dispatching:", addAction(10)); next(addAction(5)); Console. log(" new state:", store.getState()); } store.dispatch = dispatchAndLog;Copy the code

How to improve the rendering efficiency of components

As we know, React achieves minimal updates to the DOM based on the perfect combination of virtual DOM and efficient Diff algorithm. In most cases, React renders the DOM efficiently enough for our daily business. However, for complex business scenarios, performance issues still plague us. Some measures need to be taken to improve performance, and avoiding unnecessary rendering is a common optimization tool in the business.

10.1 Implementation Scheme

We learned that the trigger time of render simply means that the class component will cause render by calling the setState method. Once the parent component makes render, the child component must also perform render. Rendering of the parent component results in rendering of the child component, and the child component does not change at all. In this case, unnecessary rendering can be avoided by the following methods:

  • shouldComponentUpdate
  • PureComponent
  • React.memo

10.2 Involves life cycle functions

102.1 shouldComponentUpdate

ShouldComponentUpdate lifecycle function is used to compare the state and props to determine whether to rerender. By default, return true to re-render, or false if you do not want the component to re-render.

10.2.2 PureComponent

ShouldComponentUpdate is similar to shouldComponentUpdate, implement shouldComponentUpdate by comparing the props and state, source code is roughly as follows:

if (this._compositeType === CompositeTypes.PureClass) { shouldUpdate = ! shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState); }Copy the code

ShallowEqual corresponding method source code is as follows:

const hasOwnProperty = Object.prototype.hasOwnProperty; /** * is used to determine if two values are equal, Why so to write to the MDN document * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is * / function is(x: mixed, y: mixed): boolean { if (x === y) { return x ! == 0 || y ! == 0 || 1 / x === 1 / y; } else { return x ! == x && y ! == y; }} function shallowEqual(objA: mixed, objB: mixed): Boolean {return true; } if (typeof objA ! == 'object' || objA === null || typeof objB ! == 'object' || objB === null) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); False if (keysa.length! == keysB.length) { return false; } for (let I = 0; i < keysA.length; i++) { if ( ! hasOwnProperty.call(objB, keysA[i]) || ! is(objA[keysA[i]], objB[keysA[i]]) ) { return false; } } return true; }Copy the code

10.2.3 React. Memo

The React.memo is used to cache component renderings to avoid unnecessary updates. It is also a higher-order component, similar to PureComponent. The difference, however, is that the Memo can only be used for function components.

import { memo } from 'react';

function Button(props) {
  // Component code
}

export default memo(Button);
Copy the code

If you need a deeper comparison, then you can pass the comparison function to the memo second parameter.

function arePropsEqual(prevProps, nextProps) {
  // your code
  return prevProps === nextProps;
}

export default memo(Button, arePropsEqual);
Copy the code

10.3 summarize

In the actual development process, front-end performance is a problem that must be considered. With the complexity of business, the probability of encountering performance problems also increases.

In addition, it is recommended to granulate the page even smaller. If one is too large, it will render the entire large component when the state changes, while the granularity of the component is smaller when the component is split, which also reduces unnecessary rendering of the sub-components.

11. Understanding of Fiber architecture

11.1 background

The JavaScript engine and the page rendering engine are mutually exclusive, and when one thread executes, the other can only hang and wait. If the JavaScript thread is tied to the main thread for a long time, the render level update will have to wait for a long time, and the interface will not be updated for a long time, resulting in poor responsiveness of the page and the user may feel stuck.

And this is exactly the problem faced by the Stack Reconciler in React 15, when React renders components in a seamless process from start to completion, without interruption. If the component is large, the JS thread will execute until the entire VDOM tree has been evaluated before handing it to the rendering thread. This can lead to tasks such as user interactions, animations, and so on that can’t be handled immediately, resulting in a stuttering situation.

11.2 the React Fiber

Eact Fiber is a major change and optimization of React that Facebook has spent more than two years making. It is a re-implementation of the React core algorithm. React Fiber is being released in React 16, as confirmed from Facebook at the React Conf 2017 conference.

React does the following:

  • A higher priority task can interrupt a lower priority task. Then re-execute the low-priority task.
  • – Added asynchronous tasks that call the requestIdleCallback API when the browser is idle.
  • The DOM diff tree becomes a linked list, one DOM for two fibers (a linked list) for two queues, all for finding interrupted tasks and re-executing them.

From an architectural perspective, Fiber is a rewrite of the React core algorithm, known as the harmonic process. From the perspective of encoding, Fiber is a data structure defined internally by React. It is the node unit of the Fiber tree structure, which is also the virtual DOM under the new React 16 architecture.

Fiber is a JavaScript object that contains information about an element, its update queue, and its type. Its data structure looks like this:

Type Fiber = {// This tag is used to mark the WorkTag type of Fiber. It mainly indicates the component type represented by Fiber, such as FunctionComponent and ClassComponent: WorkTag, / / ReactElement key key: null | string, / / ReactElement type, called ` createElement method ` the first parameter to the elementType: Any, // The resolved function/class/ associated with this fiber. StateNode: any, // represents the element instance of the current FiberNode. StateNode: any, // points to 'parent' in the Fiber node tree, which returns back after processing the node: Fiber | null, / / to her first child node child: Fiber | null, structure of / / to his brother, brother return point to the same parent node (: Fiber | null, index: number, ref: null | (((handle: mixed) => void) & { _stringRef: ? String}) | RefObject, / / the current component in the process of handling props object pendingProps: any, / / the last rendering done props memoizedProps: Any, / / the Fiber corresponding components produced by the Update will be stored in the queue inside updateQueue: updateQueue < any > | null, / / the last rendering state memoizedState: Any, / / a list, hold the Fiber depends on the context firstContextDependency: ContextDependency < mixed > | null, mode: TypeOfMode, // Effect // Used to record SideEffect effectTag: SideEffectTag, // Single linked list used to quickly find the next SideEffect nextEffect: Fiber | null, / / subtree of the first side effect firstEffect: Fiber | null, / / the last one in the subtree side effect lastEffect: Fiber | null, / / representative task which point in time in the future should be complete, later renamed the lanes expirationTime: ChildExpirationTime: ExpirationTime = context = context = context = context = context = context = context = context Fiber | null, }Copy the code

11.3 Solutions

Fiber breaks up the render update process into sub-tasks, doing a little bit at a time to see if there’s time left, and if there’s time to move on to the next task; If not, suspend the current task, give time control to the main thread, and continue executing it when the main thread is less busy.

In other words, it can interrupt and restore, and reuse the previous intermediate state after recovery, and assign different priorities to different tasks. The update unit of each task is the Fiber node corresponding to the React Element.

Realization way of the above is requestIdleCallback method, window. RequestIdleCallback () method will be called function in browser free time line. This enables developers to perform background and low-priority work on the main event loop without affecting the delay of critical events such as animations and input responses.

First, React splits tasks into multiple steps and completes them in batches. After completing a portion of the task, hand back control to the browser, giving it time to render the page again. React allows the browser to run out of time and then proceed with tasks left unfinished by React. This is a cooperative scheduling approach.

The implementation process is based on Fiber nodes. As a static data structure, each Fiber node corresponds to a React Element, which stores the type of the component (function component/class component/native component, etc.) and corresponding DOM node information. As a dynamic unit of work, each Fiber node holds the component’s changed state and work to be performed during this update.

Each Fiber node has a React element. Multiple Fiber nodes build a tree based on the following three properties.

// Point to parent Fiber node this.return = null // point to child Fiber node this.sibling = null // point to first sibling on the rightCopy the code

How to optimize React performance

12.1 render to render

React has high performance thanks to the Virtual DOM and diff algorithms, but in some cases performance can be significantly improved. As we know, the class component causes render by calling setState, and once the parent component render, the child component must render as well. When we want to update a child component, such as the content of the updated green section:Ideally, we would just call render of the component in that path and perform the render of the corresponding component.However, the default for React is to call render on all components and compare the generated virtual DOM.Therefore, the default approach is very wasteful of performance.

12.2 Optimization Scheme

Nio avoids unnecessary render, which we described earlier can be optimized with shouldComponentUpdate, PureComponent, React. Memo. In addition, the following are common performance optimizations:

  • Avoid using inline functions
  • Use React Fragments to avoid extra markup
  • Using Immutable
  • Lazily loaded component
  • Event binding mode
  • Server side rendering

12.2.1 Avoid using inline functions

If we use inline functions, a new function instance is created each time the render function is called, such as:

import React from "react";

export default class InlineFunctionComponent extends React.Component {
  render() {
    return (
      <div>
        <h1>Welcome Guest</h1>
        <input type="button" onClick={(e) => { this.setState({inputValue: e.target.value}) }} value="Click For Inline Function" />
      </div>
    )
  }
}
Copy the code

Instead, you should create a function inside the component and bind events to the function itself. This will not create a separate function instance each time Render is called.

import React from "react"; export default class InlineFunctionComponent extends React.Component { setNewStateData = (event) => { this.setState({ inputValue: e.target.value }) } render() { return ( <div> <h1>Welcome Guest</h1> <input type="button" onClick={this.setNewStateData}  value="Click For Inline Function" /> </div> ) } }Copy the code

12.2.2 Use React Fragments to avoid additional markup

When a user creates a new component, each component should have a single parent label. The parent cannot have two tags, so there is a common tag at the top, so we often add an extra div tag at the top of the component.

This extra tag serves no purpose other than to act as a parent tag, and you can use fragement instead. It does not introduce any additional tags into the component, but it can act as a parent tag.

export default class NestedRoutingComponent extends React.Component {
    render() {
        return (
            <>
                <h1>This is the Header Component</h1>
                <h2>Welcome To Demo Page</h2>
            </>
        )
    }
}
Copy the code

12.2.3 Lazily Loading Components

From an engineering perspective, WebPack has code-splitting capabilities that allow you to create multiple packages for your application and load them dynamically at run time, reducing the size of the initial package. Suspense and lazy components are used to split code in React.

const johanComponent = React.lazy(() => import(/* webpackChunkName: "johanComponent" */ './myAwesome.component')); export const johanAsyncComponent = props => ( <React.Suspense fallback={<Spinner />}> <johanComponent {... props} /> </React.Suspense> );Copy the code

12.2.4 Server Rendering

Using server-side rendering mode, users can see the rendered page more quickly. React render the root component into a string and output it to the response using express or KOA renderToString:

import { renderToString } from "react-dom/server"; import MyPage from "./MyPage"; app.get("/", (req, res) => { res.write("<! DOCTYPE html><html><head><title>My Page</title></head><body>"); res.write("<div id='content'>"); res.write(renderToString(<MyPage/>)); res.write("</div></body></html>"); res.end(); });Copy the code

The client then uses the Render method to generate the HTML.

import ReactDOM from 'react-dom';
import MyPage from "./MyPage";
ReactDOM.render(<MyPage />, document.getElementById('app'));
Copy the code

React server render

13.1 What is Server-side rendering

Server rendering refers to the process of stitching together the HTML structure of a page by the service side, sending it to the browser, and then binding its state and events to become a fully interactive page. There are two main problems it solves:

  • SEO, because search engine crawler crawler tools can directly view fully rendered pages
  • Accelerate the loading of the first screen to solve the problem of a blank first screen

13.2 how to do

In React, SSR is implemented in two main forms:

  • Manually build an SSR framework
  • Use mature SSR frameworks such as next.js

The following to manually build an SSR framework to explain how to achieve SSR. First, start an app.js file with Express that listens for requests from port 3000 and returns HTML when requesting the root directory as follows:

const express = require('express') const app = express() app.get('/', (req,res) => res.send(` <html> <head> <title>ssr demo</title> </head> <body> Hello world </body> </html> `)) app.listen(3000, () => console.log('Exampleapp listening on port 3000! '))Copy the code

Then write the React code in the server and reference it in app.js:

import React from 'react'

const Home = () =>{

    return <div>home</div>

}

export default Home
Copy the code

To enable the server to recognize JSX, you need to package and transform the project using Webpakc, create a configuration file, webpack.server.js, and configure it, as shown below.

//node path module const nodeExternals = require('webpack-node-externals') module.exports = {const path = require('path') //node path module Target :'node', mode:'development', // Development mode entry:'./app.js', // output: Resolve (__dirname,'build') // Store it in the build folder of the root directory}, externals: [nodeExternals()], // maintain the references required in node module: {rules: [{// package rules test: /\.js?$/, // Exclude: // exclude: /node_modules/,// do not pack js files in node_modules. Options: {presets: ['react','stage-0',['env', {// Loader with additional packaging rules, convert react,JSX, ES6 targets: {browsers: ['last 2versions'] // Compatibility with the latest two versions of the main browser}}]]}}Copy the code

The React component is then parsed to Html using renderToString, a server-side rendering method, via the React dom.

Import express from 'express' import React from 'React '// Introduce React to support JSX syntax import {renderToString} from 'react-dom/server'// introduce renderToString import Home from'./ SRC /containers/Home' const app= express() const content = renderToString(<Home/>) app.get('/',(req,res) => res.send(` <html> <head> <title>ssr demo</title> </head> <body> ${content} </body> </html> `)) app.listen(3001, () => console.log('Exampleapp listening on port 3001! '))Copy the code

In the above procedure, you were able to successfully render the component onto the page. However, some methods, such as event handling, cannot be done on the server side, so the component code needs to be executed again in the browser. This way the server and client share the same set of code is called isomorphism. Refactoring is colloquially a set of React code that runs once on the server and again in the browser:

  • The server renders the page structure
  • Browser-side rendering completes the event binding

The way browsers implement event binding is to let the browser pull the JS file to execute and let the JS code take control, hence the need to introduce script tags. Use the script tag to import the client’s react code for the page, and configure the route for the JS file through the static middleware of Express. Modify as follows:

Import express from 'express' import React from 'React' import {renderToString} Import Home from'./ SRC /containers/Home' const app = express() app.use(express.static('public')); Const Content = renderToString(<Home/>) const Content = renderToString(<Home/>) app.get('/',(req,res)=>res.send(` <html> <head> <title>ssr demo</title> </head> <body> ${content} <script src="/index.js"></script> </body> </html> `)) app.listen(3001, () =>console.log('Example app listening on port 3001! '))Copy the code

Create webpack.client.js as the webpack configuration file for the client’s React code:

//node path module.exports = {mode:'development', // development mode entry:'./ SRC /client/index.js', Path :path.resolve(__dirname,'public') // Save to the build folder in the root directory}, module: {rule: [{// exclude: [{// exclude: [//] exclude: [//] // exclude: [//] // exclude: [//] // /node_modules/, // do not pack js files in node_modules. Options: {presets: ['react','stage-0',['env', {// Loader uses additional packing rules. Here convert react,JSX targets: {browsers: ['last 2versions'] // Compatibility with the latest two versions of the main browser}}]]}}Copy the code

Using this method, you can easily render the React server on the home page, as shown below.Generally, routes exist in an application. The configuration information is as follows:

Import React from 'React' // import JSX import {Route} from 'React -router-dom' // import Route import Home from './containers/Home' // Import Home components export default (<div> <Route path="/" exact component={Home}></Route> </div>)Copy the code

Route information can then be referenced via index.js as follows:

import React from 'react' import ReactDom from 'react-dom' import { BrowserRouter } from'react-router-dom' import Router  from'.. /Routers' const App= () => { return ( <BrowserRouter> {Router} </BrowserRouter> ) } ReactDom.hydrate(<App/>, document.getElementById('root'))Copy the code

There is an error message on the console because each Route component is wrapped with a div that is not present in the code returned by the server. Instead of BrowserRouter, use StaticRouter and pass parameters through context.

Import express from 'express' import React from 'React '// Introduce React to support JSX syntax import {renderToString} from 'react-dom/server' import {StaticRouter} from 'react-router-dom' import router from '.. /Routers' const app = express() app.use(express.static('public')); Get ('/',(req,res)=>{const Content = renderToString((// pass the current path <StaticRouter location={req.path} context={{}}> {Router} </StaticRouter>)) res.send(' < HTML >  <head> <title>ssr demo</title> </head> <body> <div id="root">${content}</div> <script src="/index.js"></script> </body>  </html> `) }) app.listen(3001, () => console.log('Exampleapp listening on port 3001! '))Copy the code

13.3 summarize

The React server rendering principle is not complicated as follows: The Node Server receives the request from the client, obtains the url path of the current request, searches for the component in the existing routing table, obtains the requested data, and passes the data to the component as props, context, or store.

Then, renderToString(), a server rendering method built into React, renders the component as an HTML string, injecting the data into the browser before exporting the final HTML.

The browser starts rendering and node comparison, then performs the event binding within the component and some interactions, and the browser reuses the HTML nodes that the server outputs, and the process ends.