preface

React is used in previous projects. If there is any mistake in this article, I hope to point out. At the end of the article are links to reference documentation.

React Basics

jsx/tsx

Special notation exists in JSX

  • The className of the element: className
<span className='span_box' />
// Reason: Class is a Javascript reserved keyword
Copy the code
  • Property to specify which form element label is bound to: htmlFor
/ / JSX writing
<form>
  <label htmlFor="male">Male</label>
  <input type="radio" name="sex" id="male" />
</form>

// Traditional HTML
<form>
  <label for="male">Male</label>
  <input type="radio" name="sex" id="male" />
</form>
Copy the code
  • Property specifies the character code to use when submitting the form: acceptCharset
/ / JSX writing
<form action="form.html" acceptCharset="ISO-8859-1"> First name: <input type="text" name="fname"><br> Last name: <form type="text" name="lname"><br> <form type=" value "="submit" > accept-charset="ISO-8859-1"> First name: <input type="text" name="fname"><br> Last name: <input type="text" name="lname"><br>Copy the code
  • Property provides HTTP headers for the information/value of the Content property: httpEquiv
/ / JSX writing
<meta httpEquiv="X-UA-Compatible" content="ie=edge" />

// Traditional HTML
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
Copy the code

Node conditional rendering

interface IProps{
	show: boolean
    [key: string] :any
}

const App: React.FC = (props: IProps) = > {
  const { show } = props
  
  return (
    <div className="App">
      {
      	show ? <div>Display content</div> : <div>Hidden content</div>
      }
    </div>
  );
  
export default App;

Copy the code

Virtual dom

  • What the virtual DOM looks like
// React virtual DOM
const reactVNode = {
	key: null.props: {
    	children: [	/ / child elements
        	{ type: 'span'./ *... * / },
            { type: 'span'./ *... * /}].className: 'kisure'.// Attributes on the tag
        onClick: () = > {}	/ / event
    },
    ref: null.type: 'div'	// Label name or component name
    // ...
}
/ / the vue virtual dom
const vueVnode = {
	tag: 'div'.// Label name or component name
    data: {
    	class: 'kisure'.// Attributes on the tag
        on: {
        	click: () = > {} / / event}},children: [{tag: 'span'./ *... * / },
        { tag: 'span'./ *... * /}]// ...
}
Copy the code
  • Generation of the virtual DOM
// react jsx
<div className='kisure' onClick={() = >{}} ><span />
    <span />
</div>
// vue template
<template>
	<div class='kisure' @click="fn">
		<span />
        <span />
	</div>
</template>
Copy the code
// react creates a virtual DOM with Babel and converts it to createElement form
React.createElement('div', { className: 'kisure'.onClick: () = > {} }, [
	React.createElement('span', { / *... * / }),
    React.createElement('span', { / *... * /})])// Vue creates the virtual DOM (only h can be obtained in the render function) and converts it into h form by vue-loader
h('div', {
	class: 'kisure'.on: {
    	click: () = > {}
    }
}, [ h('span', { / *... * /}) ], h('span', { / *... * /})])Copy the code
  • Advantages of the virtual DOM

(1) Reduce the number and scope of DOM operations:

Times: The virtual DOM can combine multiple operations into a single operation. For example, react renders the list with an array at once, and changes the values or properties of multiple nodes at once. In the traditional way of manipulating the DOM, if you want to modify hundreds of nodes, you need to manually modify them hundreds of times. Scope: The virtual DOM uses Diff to omit unnecessary operations. (For example, if there are thousands of list nodes on the page, but we want to add 10 more nodes, using **innerHTML** will repeat the rendering. React: The virtual DOM is essentially a JS object, so you can convert DOM elements into Android views, etc

  • Disadvantages of the virtual DOM

Additional create functions (react. createElement, h) are required, but you can use the React JSX language for ease of writing

Method or VUE uses **vue-loader** to convert

The state and the props

  • state

**state** is used to control behavior, update data, and render the interface. Because the component cannot modify the props passed in, it needs to log its own data changes. ** State ** contains all properties representing changes to the component UI

// The state case
class Parent extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	count: 1}}private addCount() {
    	this.setState((prevState) = > {
        	count: prevState.count + 1})}render() {
    	return <div>
          	<p>The value of count is: {this.state.count}</p>
            <button onClick={()= > this.addCount()}>count++</button>
        </div>}}Copy the code
  • props
React // functions are props and can also be used for component communication

/ / the parent component
class Parent extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	count: 1}}private addCount() {/ *... * /}
    
    render() {
    	const childProps: IChildProps = {
        	emitFunc: this.addCount,
            count: this.state.count
        }
    	return <div>
          	<Child {. childProps} / >
        </div>}}/ / child component
class Child extends React.Component<IChildProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	num: 2}}render() {
    	return <Child>
        	<p>Count: {this.props. Count}</p>
            <button onClick={()= > this.props.emitFunc()}>count++</button>
        </Child>}}Copy the code
  • The difference between props and state

**props and state are both JS objects, but the props are derived from the parent component (similar to function parameters). State ** is owned by the component itself.

(2) Can be modified: **props cannot be modified, and state cannot be modified directly, but can be modified by setState** method.

The life cycle

New edition life cycle

  • Execution order

(1) Component creation

constructor -> getDerivedStateFromProps -> render -> componentDidMount



(2) Component update

getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate



(3) Component uninstallation

componentWillUnmount

  • getDerivedStateFromProps

(1) the static getDerivedStateFromProps (nextProps, prevState) : Receives props and the previous state of the component as passed by the parent, and either returns an object to update state or null to indicate that the props received has not changed and does not need to update state.
































/ / getDerivedStateFromProps case
/ / the parent component
class Parent extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	count: 1}}private addCount() {
    	this.setState((prevState) = > {
        	count: prevState.count + 1})}render() {
    	return <div>
        	<button onClick={()= > this.addCount()}>count++</button>
          	<Child count={this.state.count}/>
        </div>}}/ / child component
class Child extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	num: 2.count: null}}static getDerivedStateFromProps(nextProps,prevState) {
    	if (nextProps.count > prevState.num) {
        	return {
            	count: nextProps.count
            }
        }
        
        return null
    }
    
    render() {
    	return <Child>
        	child demo
        </Child>}}// Parent component, click the button to increase the count state value, when the state value is larger than the child component's num, map the child component's count state
Copy the code
  • getSnapshotBeforeUpdate

This lifecycle hook must have a return value, which will be used as the third parameter

Pass to **componentDidUpdate. Must be used with componentDidUpdate**, otherwise an error will be reported

This lifecycle hook is called after render** and before DOM and refs are updated

















The old version life cycle

  • Execution order

(1) Component creation

The constructor – > componentWillMount – > componentDidMount – > componentWillUnmount (2) the component update componentWillReceiveProps – > Shouldcomponentwillupdate -> componentWillUpdate -> Render (3) componentWillUnmount

Life Cycle Brief

  • componentWillMount

It may be executed multiple times during rendering

  • componentDidMount
// There is always only one render, usually side effects at componentDidMount, for asynchronous operations
componentDidMount() {
	fetch(/*url... * /).then(res= > res.json()).then(users= > {
    	this.setState(() = > {
        	return {
            	users
            }
        })
    })
}
Copy the code
  • shouldComponentUpdate

(1) For performance optimization, return true to indicate that the component can be updated, return false to indicate that the component does not update.














  • componentWillUnmount

(1) Complete component uninstallation and data destruction here


/* Sometimes we get this warning: Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component. Cause: The Ajax request returns setState in the component and the component is destroyed before the request is completed */
componentDidMount() {
	this.lock === true
    axios.post().then((res) = > {
    	this.isMount && this.setState({   // Add condition ismount to true
        	aaa:res
      	})
    })
}

componentWillUnmount() {
    this.isMount === false
}
Copy the code
  • Update order of parent and child components

The parent component fires **componentWillMount** first, and then the child component.


How to request data and change state when external props change

  • Method 1: use componentWillReceiveProps load data (has) is not recommended
class Demo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	num: 2}}componentWillReceiveProps(nextProps) {
    	if (/* Meets some conditions */) {
        	this.loadData()
        }
    }
    
    private loadData() {}
    
    render() {
    	return <Child>
        	child demo
        </Child>}}Copy the code
  • Method 2: Use getDerivedStateFromProps + componentDidUpdate to load data
class Demo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        
        this.state = {
        	num: 2}}static getDerivedStateFromProps(nextProps,prevState) {
    	if (nextProps.count > prevState.num) {
        	return {
            	count: nextProps.count
                / /...}}return null
    }
    
    private loadData() {}
    
    componentDidMount() {
    	this.loadData()
    }
    
    componentDidUpdate(prevProps, prevState) {
    	if (/* Meets some conditions */) {
        	this.loadData()
        }
    }
    
    render() {
    	return <Child>
        	child demo
        </Child>}}Copy the code

Common problems encountered during development

  • Asynchronous requests should be placed in componentDidMount

Due to asynchronous rendering introduced in future versions of React, the dom can be interrupted and restarted before it is mounted, Cause componentWillMount, componentWillUpdate, componentWillReceiveProps * * * * in an update may be triggered multiple times, So those side effects that you only want to trigger once should be placed in **componentDidMount**.

  • Props changes the trigger lifecycle

The most common misconception is * * getDerivedStateFromProps and componentWillReceiveProps will only change after changing the props will call. In fact, both lifecycle functions are called again whenever the parent component is re-rendered, updating the props** with or without changes.

Lifecycle version migration

Will React in version 17.0 will remove the three life cycle: componentWillMount, componentWillReceiveProps and componentWillUpdate

PureComponent, Component

(1)**PureComponent improves performance because it reduces the number of render operations in the application. Because the React. PureComponent * *

ShouldComponentUpdate () implements a shallow-comparison of props and state. ShouldComponentUpdate () is not implemented.

(2) It is important to note that when we use **PureComponent, we need props and state** to be basic types. If these two are

Complex data structures (e.g., nested objects and arrays) may render the wrong results.

(3) If you have to use a complex data structure, you must manually enforce the **forceUpdate** update

The channel data has changed. (4) shouldComponentUpdate() in react. PureComponent skips props updates for all subcomponent trees. PureComponent is not rerendered if the parent Component is not rerendered), so a Component that uses react. PureComponent must have all its children be react. PureComponent**

setState

  • define

1. Stuff the changes that need to be handled into the component’s **state** object

2. Tell the component and its children that it needs to be rerendered with the updated state 3. Respond to event handling and the main way the server responds to updating the user interface

  • SetState writing
// The old way
this.setState({
	count: this.count + 1
})

// Now write
this.setState((prevState) = > {
	return { count: prevState.count + 1}})/* Function/functions/functions/functions/functions/functions/functions/functions/functions/functions/functions/functions - Function is passed. React will put functions that update state into a queue, and the functions will be called in order. This function has two parameters: prevState and props */
Copy the code
// The following examples illustrate the above points
class Demo extends Component {
	state = {
    	count: 0
    }
    
    handleObj() {
    	this.setState({
        	count: this.state.count + 1
        })
        
        this.setState({
        	count: this.state.count + 1})}handleFunc() {
    	this.setState((prevState, props) = > {
        	return {
            	age: prevState.age + 1}})this.setState((prevState, props) = > {
        	return {
            	age: prevState.age + 1}})}render() {
    	return <div>
        	<button onClick={()= > this.handleObj}>object</button>
            <button onClick={()= > this.handleFunc}>function</button>
        </div>}}When the object button is clicked, the age state value is 1. When the function button is clicked, the age state value is 2
Copy the code
  • SetState does not immediately change the React state value
class Kisure extends React.Component {
  constructor() {
    super(a);this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // Log the first time => 0

    this.setState({val: this.state.val + 1});
    console.log(this.state.val);    // Log the second time => 0

    setTimeout(() = > {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // Log the third time => 2

      this.setState({val: this.state.val + 1});
      console.log(this.state.val);  // Log the fourth time => 3
    }, 0);
  }
  
  render() {
    return null; }};Copy the code
  • Modifying state directly is not effective

Remember that **setState is essentially a queue mechanism for state updates. When setState is executed, the state to be updated will be merged into the status queue instead of being updated immediately. The queue mechanism can update the state in batches. If you do not use setState** to modify directly

This. state, then this **state will not be put into the state queue, the next time we call setState to merge the state queue, we will ignore the state that was changed directly before the merge, and we will not be able to merge, and we will not actually update the state** that you want.

  • What happens after setState

React diff the state to determine if it has changed, and then diff the DOM to decide whether to update the UI. SetState frequently over a short period of time. React pushes state changes onto the stack and updates state** and views in batches when appropriate to improve performance.

  • How do I know if state is updated
// Pass in the callback function
setState({
    age: 1
}}, function(){
    console.log(this.state.age);
})

// Life hook
componentDidUpdate(){
    console.log(this.state.age);
}
Copy the code

React Event mechanism

The react event flow chart is shown above

  • Event registration and storage

As shown in the figure above, event registration process:

  • When a component is loaded or unloaded, use **lastProps,nextProps** determines whether events are added or deleted.
  • If the event is new, call **EventPluginHubtheenqueuePutListener** For event storage.
  • Access to * *documentObject based on the React event (onClick,onCaptureClick) to determine whether to bubble or capture. Then with the help ofaddEventListenertodocumentRegister the event and set the callback function todispatchEvent**(this method is used for event distribution)

As shown above, is the event store:

  • Because the React component is loaded and updated, events are added or deleted. It will enter the transaction queue and store the event (execute **)putlistener). Determines that it is a delete event, the delete listening method (deleteListener* *).
  • The method of deleting and adding listeners comes from **EventPluginHubThe React callback manages the React callbacklistener** To obtain, delete, and store.
  • Events are stored in **listenerBankWhere, the structure is:listenerBank[registrationName][key]. With the help ofEventPluginHubtheputListenerMethod is used tolistenerBankAdd a listener and get the unique identifier key of the element to which the event is bound to store it:listenerBank[registrationName][key] = listener巴拿马事件
//listenerBank[registrationName][key]
{
    onClick: {nodekey1:() = >{/ *... * /}
        nodekey2:() = >{/ *... * /}},onChange: {nodekey3:() = >{/ *... * /}
        nodekey4:() = >{/ *... * /}}}Copy the code
  • Event triggered execution

As shown in the figure above, the event triggering process: (1) triggering **document registers the callback of the native event dispatchEvent** (2) retrieving the target element of the triggering event

class Demo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
        super(props)
        
        this.state = {/ *... * /}
    }
    
    private parentClick = () = > {/ *... * /}
    
    private childClick = () = > {/ *... * /}
    
    render() {
      return <div className='parent' onClick={this.parentClick}>
                <div className='child' onClick={this.childClick}>
                  test
                </div>
              </div>}}Copy the code

Code like this:

(1) Iterate through all elements of the component to find the target element that triggered the event. (2) iterate through the parent element to store each level’s component into the ancestors array. (3) React custom bubbling process: Iterating through the array of ancestors, executing the **handleTopLevel method in turn, (4) composing the synthesized event, and then storing the event in the eventQueue queue (5) iterating through the eventQueue queue, isPropagationStopped indicating whether bubbling is prevented, If this is prevented, the traversal is stopped, otherwise the synthesized event (6) queue traversal is performed via executeDispatch**, and the processed events are cleaned up

It is important to note that stopPropagation is implemented by React itself. This method is not nativeCopy the code
  • The process of synthesizing events

As shown in the figure above, is the synthesis event flow:

(1) React extract event (2) The EventPluginHub method in the extractEvents holds the event iterated through, EventPlugin(**EventPlugin is a tool used to handle different events) (3) EventPlugin will generate different events according to the type of event, and then return to the event pool (4) and then uniquely identify the key and event type according to the element. Finally, return the resulting callback to the eventQueue in which the event was triggered in the previous step

  • Why bind this to events in the component
function invokeGuardedCallback(name, func, a) {
  try {
    func(a);
  } catch (x) {
    if (caughtError === null) { caughtError = x; }}}Copy the code

In the code above, the internal func** argument for a method called after **dispatchEvent is invoked is run without specifying which component is called. Therefore, you need to bind this manually. Otherwise, you don’t get this.

// Bind this to one of the following:
class Demo extends React.Component<IProps.IState> {
    constructor(private props: IProps) {
        super(props)
        
        this.state = {/ *... * /}
        this.childClick = this.childClick.bind(this) / / the first
    }
    
    private parentClick = () = > {/ *... * /} / / the second
    
    private childClick () {/ *... * /}
    
    private innerChildClick () {/ *... * /}
    
    render() {
      return <div className='parent' onClick={this.parentClick}>
                <div className='child' onClick={this.childClick}>
                  <div className='innerChild' onClick={()= >OnClick ={() => this.innerChildclick ()}</div>
                  <div className='innerChild' onClick={this.innerChildClick.bind(this)}>A fourth way of binding: onClick = {this. InnerChildClick. Bind (this)}</div>
                </div>
              </div>}}Copy the code
  • React events and native events
  • DOM native can use **addEventListenerThe ** function listens for events at different stages of the event stream.

The React binding event property names are humped, and the event handler is a function, whereas the native DOM event is the function name

  • Mixed Event Usage Notes:

(1) Response order: DOM event listeners are executed, then the event continues to bubble up to document, and then the synthesized event listeners are executed. React event (2) prevents bubbling: If the DOM event is prevented from bubbling, it will not reach the document, so the composite event will not fire


  • A brief summary of the React event:
  • React implements an intermediate layer of its own to avoid binding too many event handlers to the DOM, which can affect the overall page response and memory footprint, and to avoid low-level event system differences between browsers. When the user adds a function to onClick, React does not bind the Click time to the DOM, but to **documentAll events supported by listening on. When the event occurs and bubbles todocumentWhen react is useddispatchEvent** Finds the corresponding event to handle.
  • Listeners for synthesized events are uniformly registered in **document**, and only the bubbling stage. So listeners for native events always respond earlier than listeners for synthetic events. Preventing bubbling of native events prevents listeners of synthesized events from executing.

key

class Demo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
        super(props)
        
        this.state = {
        	children: [{name: 'a'.age: 15.id: 0 },
                { name: 'b'.age: 16.id: 1 },
                { name: 'c'.age: 17.id: 2 },
                { name: 'd'.age: 18.id: 3 }
                / *... * /]}}render() {
    	const { children } = this.state
    	return <div>
        	{
            	children.map(child => {
                	return <p key={child.id}>{child.name}-{child.age}</p>})}</div>}}Copy the code

If there is a default state value **children**, use the map method to create a child list node

  • The key role

React uses a key to identify components. It’s a form of identification, just like everyone has an ID card to identify them. React considers the same key to be the same component. In this way, components corresponding to the same key will not be created later. React requires us to provide a key to help update and reduce performance overhead.

  • Index is not recommended as the key value

The value of the key is unique, and the real purpose of the key is to identify the correspondence between the elements in the previous rendering and the subsequent rendering, preventing unnecessary updates. So if we use index to identify the key, after the array is inserted, sorted, etc., the original index does not correspond to the original value.

fragments

const children: ArrayThe < {name: string; age: number; id: number[{} > =name: 'A'.age: 10.id: 1 },
    { name: 'B'.age: 11.id: 2 },
    { name: 'C'.age: 12.id: 3 },
    / *... * /
]

const Demo: React.Fc = (props: IProps) = > {
	return (
    	<div className='demo-div'>
            {
            	children.map(item => {
                	return <React.Fragment key={ item.id }>
                    	<p>{ item.name }</p>
                        <p>{ item.age }</p>
                    </React.Fragment>
                })
            }
        <div>
    )
}
Copy the code
  • Ragments allows you to aggregate a list of child elements without adding additional nodes to the DOM
  • <></>is<React.Fragment></React.Fragment>Grammar sugar, but<></>The syntax cannot accept key values or attributes. So if you want to use key, you need to use it<React.Fragment></React.Fragment>

Error boundary

The definition of the wrong boundary

The Error boundary is a React component that catches JavaScript errors that occur anywhere in its child component tree and renders the alternate UI. Error bounds catch errors during rendering, in the life cycle, and in constructors throughout the component tree

use

// The first error boundary
class ErrorBoundary extends React.Component<IProps.IState> {
    constructor(props: IProps) {
      super(props);
      
      this.state = { hasError: false };
    }
    
    componentDidCatch(error, errorInfo) {
      this.setState({
          error:error,
          errorInfo:errorInfo
      })
    }
    
    render() {
      if (this.state.errorInfo) {
        return <div>
            <p>{this.state.error&&this.state.error.toString()}</p>
            <p>{this.state.errorInfo.componentStack}</p>
        </div>
      }
      
      return this.props.children; }}Copy the code
// The second error boundary
class ErrorBoundary extends React.Component<IProps.IState> {
    constructor(Props: IProps) {
      	super(props);
      
      	this.state = { 
      		hasError: false 
      	};
    }
    
    static getDerivedStateFromError(error) {
      	return { 
      		hasError: true 
      	};
    }
    
    render() {
      	if (this.state.hasError) {
        	// Display degraded UI
        	return <div>The error!</div>
      	}
        
      	return this.props.children; }}Copy the code

Code like this:

  • Just use ** in classcomponentDidCatch()(Typo),static getDerivedStateFromError()**(render alternate UI) one of these, then, is the error bound component
  • Since React 16, any error not caught by the error boundary will cause the entire React component tree to be uninstalled. Adding error boundaries allows you to provide a better user experience when an exception occurs in your application. Where some OF the UI components crash, the rest of the UI can still interact.

Controlled components and uncontrolled components

A controlled component is a component that can be controlled by the React state.

An uncontrolled component is one that is not controlled by the React state. In React, components such as Input Textarea default to uncontrolled components (the values inside the Input box are user-controlled and have nothing to do with React). However, it can also be converted to a controlled component, by fetching the current input through the **onChange event, passing the current input as value, and then becoming a controlled component. Benefits of controlled components: You can control user input through the onChange** event and filter out improper input using regular expressions.

// Controlled components
import React, { createRef } from 'react'

class Demo extends React.Component<IProps.IState> {
	constructor(Props: IProps) {
        super(props)
      
        this.state = { 
           name: ' '.age: null
        }
        
        this.sexRef = createRef()
    }
    
    private handleNameChange = (name: string) {
    	this.setState(() = > {
        	return {
            	name
            }
        })
    }
    
    private handleAgeChange = (age: number) {
    	this.setState(() = > {
        	return {
            	age
            }
        })
    }
    
    private handleClick = () {
    	// Click to get the value of sex
    	console.log(this.sexRef.current.value)
    }
    
    render() {
    	return (
        	<form className='form-div'>
            	<div>
                	<label for='name'>Name:</label>
                    <input type='text' id='name' value={this.state.name} onChange={this.handleNameChange}/>
                </div>
                <div>
                	<label for='age'>Age:</label>
                    <input type='number' id='age' value={this.state.age} onChange={this.handleAgeChange} />
                </div>
                <div>
                	<label for='sex'></label>
                    <input type='text' id='sex' ref={this.sexRef}/>
                </div>
                <div>
                	<button onClick={this.handleClick}>Click to get the value of sex</button>
                </div>
            </form>)}}Copy the code

Code like this:

  • Controlled component, **inputthevalueProperty is bound to the value of the corresponding state and is used simultaneouslyonChange** Register event.
  • Uncontrolled components, which cannot be retrieved by state, can use **refIn order to get. inconstructorIn the callReact.CreateRef()To declarerefAnd then the declaredrefAssigned toinputThe correspondingref六四屠杀

refs

What is the refs

  • Refs is a tool to get instances of DOM nodes or React elements. In special cases (the normal case is when the parent modifies the props property passed to the child component), you need to force changes to the child component.
  • Refs usage scenarios :(1) because Refs can get instances of subcomponents, it can call methods of subcomponents. (2) Because Refs can get the DOM node, it can do some operations on the DOM node, such as focus control of the input element, and the uncontrolled component gets the value of the DOM node
  • The value of Refs behaves differently depending on the type of node :(1) when the Refs attribute is used for HTML elements, Refs receives the underlying DOM element as its current attribute. (2) When the Refs attribute is used for a class component, the Refs receives the component’s mount instance as its current attribute.
  • Note: we use **React.createRef()A created ref cannot be assigned to a function component because the function component has no instance. If you must get it, use itforwardRefMethods (mentioned in the next section), oruseRef**(react hooks)

The use of the refs

  • Create the refs
import React, { createRef } from 'react'
import CourseTreeContainer from '@ /... '

class Demo extends React.Component<IProps.IState> {
    constructor(Props: IProps) {
        super(props)
      
        this.state = { 
           name: ' '
        }
        
        this.sexRef = createRef()
        this.courseTreeRef = React.createRef()
    }
    
    private handleClick = () {
        // Click to get the value of sex
        console.log(this.sexRef.current.value)
        // Input is in focus state
        this.sexRef.current.focus()
    }
    
    // Refresh data
    private refreshDataSource = () = > {
    	// Call the subcomponent method loadFirstLayerMenu
    	(this.courseTreeRef! as any).current! .loadFirstLayerMenu();/ *... * /
    }
    
    render() {
        return (
            <form className='form-div'>
                <div>
                    <label for='sex'></label>
                    <input type='text' id='sex' ref={this.sexRef}/>
                </div>
                <div>
                    <button onClick={this.handleClick}>Click to get the value of sex</button>
                </div>
            </form>
            
            <div className='table-tree'>
                <CourseTreeContainer {. courseTreeProps} ref={this.courseTreeRef}/>
            </div>
            <button onClick={this.refreshDataSource}>The refresh data</button>)}}Copy the code

In the code above, Refs is created using ** react.createref () and attached to the React element via the ref** attribute.

If applied to a DOM element, Refs receives the underlying DOM element as its current attribute. If the scoped class component can get an instance of **CourseTreeContainer, then its internal method can be called: loadFirstLayerMenu**.

forwardRef

Before using the forwardRef** you need to know:

  • 六四屠杀forwardRef** creates a React component that can receive itrefProperty is forwarded to an internal component
  • 六四屠杀refGets an instance, either a DOM instance or a class instance. But if it is a function component, useref** to pass an error.



  • Props cannot pass ref, so ** is neededReact.forwardRefCreate a React component.
  • Why can’t props pass ref
const ErrorDemo = ({ref}) = > {
    return <div ref={ref}>
        <p>hahahha</p>
    </div>
}

const RightDemo = forwardRef((props, ref:IRef) = > {
    return <div ref={ref}>
        <p>hahahha</p>
    </div>
})

class Demo extends React.Component<IProps.IState> {
	private ref: React.Ref<any>
    
    constructor(Props: IProps) {
    	super(props)
        
        this.ref = React.createRef();
    }
    
    render() {
        return <>
        	<ErrorDemo ref={this.ref}/>
            <RightDemo ref={this.ref}/>
        </>}}Copy the code

The ErrorDemo component this.ref is always {current: null}, and the Test subcomponent also found that the ref passed in is undefined. The RightDemo** components are available.

  • ForwardRef usage mode
const FancyButton = React.forwardRef((props: IProps, ref: IRef) = > (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
))

constref = React.createRef() <FancyButton ref={ref}>Click me! </FancyButton>console.log('FancyButton', FancyButton)
Copy the code

React passes the ref of

element as the second argument to the ** react. forwardRef** function. The render function passes ref to the


  • Analysis of frost Dref principle
export default function forwardRef<Props.ElementType: React$ElementType> (
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  / / __DEV__ can watch
  if (__DEV__) {
    / *... * /
  }

  if(render ! =null) {
	/ *... * /
  }

  return {
    // the $$typeof inside the component is REACT_FORWARD_REF_TYPE
    $$typeof: REACT_FORWARD_REF_TYPE,
    //render is the FunctionComponent wrapper, ClassComponent is not forwardRef
    render,
  };
}
Copy the code

An object is eventually returned



Render is a method that is called when the function component is updated (this part involves **)renderWithHooks.bailoutHooks.reconcileChildren, there will be a special explanation later).

Note:
FancyButtonIs an object, whereas the class component that you normally create, that’s aReactElement* * objects:

/ / ReactElement object
const element = { 
	$$typeof: REACT_ELEMENT_TYPE,
    type: type,
    / *... * /
}
Copy the code

REACT_FORWARD_REF_TYPE only becomes REACT_FORWARD_REF_TYPE when the forwardRef wraps it.

Stateless components and stateful components

Stateless components:

const FuncComponent:React.Fc = props= > <div>{props.name}</div>;
Copy the code

The advantage is that it is small in size and fresh in writing. The disadvantages are stateless and lifecycle, and you can’t use the lifecycle to reduce render, which means that the props will be re-rendered whenever they change. The stateless component is used to define the template. It receives props from the parent component and uses {props. XXX} expressions to plug props into the template. Stateless components should keep templates pure to facilitate component reuse

Stateful components:

That’s class components, state and life cycle. The disadvantage is that it is large (the code of the packaged class component is larger than the code of the functional component for the same function). Stateful components are used to define interaction logic and business data.

Component communication

  • Props communicates with parent and child components
/ / the parent component
class Parent extends React.Component<IProps.IState> {
    constructor(private props: IProps) {
        super(props)
        
        this.state = {
            count: 1
        }
    }
    
    private addCount() {/ *... * /}
    
    render() {
        const childProps: IChildProps = {
            emitFunc: this.addCount,
            count: this.state.count
        }
        return <div>
            <Child {. childProps} / >
        </div>}}/ / child component
class Child extends React.Component<IChildProps.IState> {
    constructor(private props: IProps) {
        super(props)
        
        this.state = {
            num: 2}}render() {
        return <Child>
            <p>Count: {this.props. Count}</p>
            <button onClick={()= > this.props.emitFunc()}>count++</button>
        </Child>}}Copy the code

The parent component can pass properties and methods to the child component via props. **this.props. Coun is information that a parent passes to a child component to complete the parent’s communication to itself. This.props. EmitFunc ** is a method that the parent passes to the child component. The child component can execute this method once it triggers the conditions that meet the requirements and passes in related parameters

  • Context delivers communication across components

Context provides a way to pass data through the component tree, eliminating the need to manually pass props properties at every level. Context has three apis: react.createcontext (), Provider, and Consumer (1). React.createcontext () : Creates a context and contains Provider and Consumer components. (2)Provider: the producer of data, receives the stored public state through the value property, and passes it to the child component or the descendant component. (3)Consumer: the data Consumer updates the status of the current component in real time by subscribing to the value of the context passed in by the Provider.

/ / create the context
// defaultValue is the defaultValue. The defaultValue parameter takes effect only when no Provider is matched in the tree of the component.
// Note: When undefined is passed to the Provider's value, the consumer component's defaultValue does not take effect.
const defaultVal = {/ *... * /}
const DemoContext = React.createContext(defaultVal);
/ *... * /
Copy the code
// Data generation
import DemoContext from '.. /.. '

class DemoProvider extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
        super(props)
        
        this.state = {
            / *... * /
            value: {
            	color: 'pink'.'font-size': 27}}}render() {
    	const { value } = this.state
    	return (
        	<DemoContext.Provider value={ value} >
            	{ this.props.children }
            </DemoContext.Provider>)}}Copy the code
// The first data consumption method:
import DemoContext from '.. /.. / '

class DemoConsumer extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
        super(props)
        
        this.state = {
            / *... * /}}render() {
    	const { value } = this.state
    	return (
        	<DemoContext.Consumer>
            	{
                	value => (
                    	<div style={{ border: `1px solidThe ${value.color} `}} >hello kisure /*... * /</div>)}</DemoContext.Consumer>)}}// Second data consumption method
class DemoConsumerTwo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
        super(props)
        
        this.state = {
            / *... * /}}static contextType = DemoContext
    
    render() {
    	const { color } = this.context
    	return (
        	<div style={{ border: `1px solidThe ${color} `}} >hello kisure /*... * /</div>)}}Copy the code

Context.Provider: The Provider receives a value property and passes it to the consuming component. A Provider can be associated with multiple consumer components. Multiple providers can also be nested. Data in the inner layer has a higher priority and overwrites data in the outer layer. When a Provider’s value changes, all of its internal consuming components are rerendered. The Provider and its internal consumer components are not subject to the shouldComponentUpdate function. (2) Context. Consumer: There are two consumption methods: the first is in the render props form (described in a later section), and the second is mounted directly on the class **static contextType = DemoContext, so that the value in the value can be accessed through this.context**.

  • Redux for communication between non-nested components (use with React-Redux)
//reducer
function counter(state = 0, action) {
	switch (action.type) {
    	case 'INCREMENT':
      		return state + 1;
    	case 'DECREMENT':
      		return state - 1;
    	default:
      		returnstate; }}/ / create a store
import { createStore } from 'redux'
let store = createStore(counter);

//action
store.dispatch({ type: 'INCREMENT'.value: { / *.. * /}});Copy the code
// Combine redux with React-redux
import * as React from 'react'
import {connect} from 'react-redux'
import { bindActionCreators } from 'redux';
import { updateInfo } from 'store/... /action'; 
/ *... * /

class Demo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
        super(props)
        
        this.state = {
            / *... * /
        }
    }
    
    private handleClick = () = > {
    	this.props.updateInfo({
        	/ *... * /})}render() {
    	return (
        	<div>
            	<p>Value mapped from redux: {this.props. Name}</p>{/ *... * /}<button onClick={this.handleClick}>Click to update the redux status data</button>
            </div>)}}// Map the state to props
function mapStateToProps(state: any) {
    return {
    	name: state.name
    	/ *... * /
    };
}

// Map action to props
function mapDispatchToProps(dispatch: any) {
    return {
        updateInfo: bindActionCreators(updateValue, dispatch)
    };
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Demo);
Copy the code

React-redux and react-redux will be discussed later in this article.

HOC and render props

  • HOC high-level components
const HOCFactory = WrappedComponent= > {
    return class HOCDemo extends React.Component<IProps.IState> {
     	constructor(private props: IProps) {
        	super(props)
            / *... * /
        }

      	render(){
        	return <WrappedComponent {. this.props} / >}}}Copy the code

In the code above, HOC is a feature that takes a component as an argument and returns a new component. It does :(1) property proxy and (2) reverse inheritance

Property broker: Passes properties received by higher-order components to incoming components.

  • Modify * *propsThe content value of the component passed inWrappedComponent** props to control.

function DemoHOC(WrapperComponent) {
    return class extends React.Component<IProps.IState> {
    	private defaultVal: IDefaultVal
    	constructor(private props: IProps) {
            super(props)
            this.defaultVal = {/ *... * /}}render() {
            const{ name, ... props } =this.defaultVal
            return <WrapperComponent {. props} name={name| | 'kisure'} / >}}}Copy the code

This code overrides and adds props** to the WrapperComponent.

Reverse inheritance: Inherits from passed components, mainly for rendering hijack and control of **state**.

  • Reverse inheritance allows higher-order components to pass **thisKeyword acquisitionWrappedComponentSo we can getstate.props, component lifecycle hooks,render**, etc., so we mainly use it for rendering hijacking, such as reading or changing the structure of the React tree in the render method.

function DemoHOC(WrapperComponent) {
	// Inherits the incoming component
	return class extends WrapperComponent {
    	// Override the componentDidMount method
        componentDidMount(){
        	// Change the count status
      		this.setState({ count: 2 });
          	/ *... * /
        }
    	render() {
        	// Decide whether to render the component according to some parameters
          	if (this.props.showContent) {
            	return (
                	<p>Rendering hijacks presentation content</p>)}// Use super to call the render method of the incoming component
          	return super.render(); }}}Copy the code

In the code above, with the help of reverse inheritance, you can change the way the lifecycle is written, and optionally render content based on the values of **props**.

  • render props

Pass **state in a component as props** to the caller, who can dynamically decide how to render. This mode lets the user not worry about the internal implementation of the component (where everything is already done), but just pass the data out through functions and implement custom rendering.

import RenderPropsComponent from '.. /.. '

class Demo extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        / *... * /
    }
    
    render () {
    	return <div>{/* Pass a function with an argument. Get the data passed by the RenderPropsComponent */}<RenderPropsComponent render=((str: string) = > (
            	<>
                	<div>
                		<p>The current string value is {STR}.</p>
                	</div>
                </>)) / >
        </div>
    }
}
Copy the code
class RenderPropsComponent extends React.Component<IProps.IState> {
	constructor(private props: IProps) {
    	super(props)
        / *... * /
    }
    
    render() {
        const { render} = this.props;

        return (
            <div>
              {render('hello kisure this is a rende rprops demo')}
            </div>)}}Copy the code

As shown in the code above, Render is the props of the RenderPropsComponent. Inside this component, located at render(), the render method passed by props is executed. So we can get the corresponding data in Demo**.

Suspense and React. Lazy ()

  • React.lazy()

The React core library implements code splitting to lazily load the React component. You can also use **react-loadable** to create the same effect.

  • Suspense

Suspense **Suspense is used with react.lazy (). It is used to wrap lazy-loaded components. Multiple lazy-loaded components can be packaged in a suspense component. Suspense** also provides a fallback attribute to make react elements explicit during lazy loading of components. In Suspense** *, loading takes time because of lazy loading. Improve the user experience).

/ / child component
const ChildComponent: React.Fc = (props: IProps) = > {
	return (
    	<div>
        	<p>hello this is a child component</p>
        </div>)}/ / the parent component
const Child = React.lazy(() = > import('./ChildComponent'));

const Parent: React.Fc = (props: IProps) = > {
	return (
        <div>
            <Suspense fallback={<div>Loading desperately, please wait...</div>} ><Child />
            </Suspense>
        </div>
    );
}
Copy the code
  • The principle of analysis
export function lazy<T.R> (ctor: () => Thenable<T, R>) :LazyComponent<T> {
  let lazyType = {
    $$typeof: REACT_LAZY_TYPE,
    _ctor: ctor,
    // React uses these fields to store the result.
    _status: -1._result: null};return lazyType;
}

function readLazyComponentType(lazyComponent) {
  const status = lazyComponent._status;
  const result = lazyComponent._result;
  switch (status) {
    case Resolved: { // Resolve renders the corresponding resource
      const Component = result;
      return Component;
    }
    case Rejected: { // Rejected throws an error
      const error = result;
      throw error;
    }
    case Pending: {  // Pending throws thenable
      const thenable = result;
      throw thenable;
    }
    default: { // Go here for the first time
      lazyComponent._status = Pending;
      const ctor = lazyComponent._ctor;
      const thenable = ctor(); // You can see a mechanism similar to Promise
      thenable.then(
        moduleObject= > {
          if (lazyComponent._status === Pending) {
            constdefaultExport = moduleObject.default; lazyComponent._status = Resolved; lazyComponent._result = defaultExport; }},error= > {
          if(lazyComponent._status === Pending) { lazyComponent._status = Rejected; lazyComponent._result = error; }});// Handle synchronous thenables.
      switch (lazyComponent._status) {
        case Resolved:
          return lazyComponent._result;
        case Rejected:
          throw lazyComponent._result;
      }
      lazyComponent._result = thenable;
      throwthenable; }}}Copy the code
function import(url) {
  return new Promise((resolve, reject) = > {
    const script = document.createElement("script");
    const tempGlobal = "__tempModuleLoadingVariable" + Math.random().toString(32).substring(2);
    script.type = "module";
    script.textContent = `import * as m from "${url}"; window.${tempGlobal}= m; `;

    script.onload = () = > {
      resolve(window[tempGlobal]);
      delete window[tempGlobal];
      script.remove();
    };

    script.onerror = () = > {
      reject(new Error("Failed to load module script with URL " + url));
      delete window[tempGlobal];
      script.remove();
    };

    document.documentElement.appendChild(script);
  });
}
Copy the code
class Suspense extends React.Component {
  state = {
    promise: null
  }

  componentDidCatch(e) {
    if (e instanceof Promise) {
      this.setState({
        promise: e
      }, () = > {
        e.then(() = > {
          this.setState({
            promise: null})})})}}render() {
    const { fallback, children } = this.props
    const { promise } = this.state
    return <>
      { promise ? fallback : children }
    </>}}Copy the code

(1) Why can code segmentation be realized

Dynamic import, like **Promise implementation mechanism, has three states: Pending, Resolved, and Rejected**. Import () returns a promise, and when Webpack parses into import() syntax, the code splits automatically.

(2) the React. Lazy () analysis

ReadLazyComponentType () is the LazyComponent object returned after react.lazy () is executed. This object has the **_status attribute, which defaults to -1 initially. At initial rendering, the code for default in readLazyComponentType is executed, that is, import(URL) is executed. Throw exists in Suspense because Suspense can catch the information thrown. If catch is a Promise, children are rendered as fallback values, and once the Promise is resolved, the final result is rendered again. The default** step and the **Pending step are for Suspense** scenarios.

(3) analysis of Suspense

Because ** react.lazy () throws error information, Suspense needs to use life cycle ComponentDidCatch to catch error exceptions in the subcomponent tree. So when the parent finds an asynchronous request while rendering the child, it throws an error directly and catches the Promise object, rendering the fallback. When resolve is rerendered, the next asynchronous request repeats until the entire parent component throws a promise of resolve, replacing loading** with the real component.

hooks

  • The cause of hooks

(1) Methods in the class component need to bind this.

(2) It is too difficult to reuse a stateful component. (3) Due to changes in business requirements, functional components have to be changed to class components.

  • useState

**setState: Allows a function component to have the state and setState functions of a class component.

function UseStateDemo: React.Fc = (props: IProps) = >{
	const [count, setCount] = useState(1)
    
    const addCount = (count: number) = > {
    	setCount(count)
    }
    
    return (
    	<div>
        	<p></p>
            <button onClick={()= >AddCount (count + 1)}> click addCount</button>
        </div>)}Copy the code

When using **useState, note that useState is executed sequentially. If you use useState in an if or loop statement, the current useState will be skipped if a condition is not met. The impact of this is that the value of state will be confused (for example, there are three useState values for ABC, and B is in the if condition. If the if condition is not met for some reason, B will be skipped and C will be continued. This causes the state of B to be retrieved in C). This is due to the fact that the hooks objects generated by useState are internally linked lists, and getting the state value depends on next(which points to the next state**). The source code analysis of useState is provided in the following sections.

  • useEffect

**useEffect: Assumes the role of the function component lifecycle.

function UseEffectDemo: React.Fc = (props: IProps) = >{
	const [count, setCount] = useState(1)
    
    useEffect(() = > {
    	conosle.log('componentDidMount')
        return () = > {
        	console.log('componentWillUnmount')
        }
    }, [])
    
    useEffect(() = > {
    	console.log('componentDidUpdate')
    }, [count])
    
    useEffect(() = > {
    	console.log('componentDidUpdate')})return (
    	<div>
        
        </div>)}Copy the code

When we use **useEffect, if the second argument is an empty array, it behaves as componentDidMount. If a useEffect callback returns a function, the behavior of the return is componentWillUnmount. UseEffect is used as componentDidUpdate if the second argument to useEffect is passed in an array with either a status field or nothing in it. React will be called repeatedly if the react implementation is updated after the react test. (2)useEffect** also has all hidden rules:

  • 六四屠杀useEffectIt’s used instateThe value of theta is fixed atuseEffectThe interior will not be changed unlessuseEffectRefresh and re-fixstateThe value of * *.
function UseEffectDemo: React.Fc = (props: IProps) = >{
	const [count, setCount] = useState<number> (0)
    useEffect(() = > {
        console.log('use effect... ', count)
        const timer = setInterval(() = > {
            console.log('timer... count:', count)
            setCount(count + 1)},1000)
        return () = > clearInterval(timer)
    },[])
    return (
    	<div>{count}</div>)}Copy the code
  • Do not include ** in the judgment conditionuseEffect六四屠杀
function UseEffectDemo: React.Fc = (props: IProps) = >{
	const [count, setCount] = useState<number> (0)
    if(0){
       useEffect(() = > {
            console.log('use effect... ',count)
            const timer = setInterval(() = > setCount(count +1), 1000)
            return () = > clearInterval(timer)
        }) 
	}
    return (
    	<div>{count}</div>)}Copy the code
  • 六四屠杀useEffect** cannot be interrupted
function UseEffectDemo: React.Fc = (props: IProps) = >{
	const [count, setCount] = useState<number> (0)
    useEffect(() = > {
        / *... * /
    })
    return
    useEffect(() = > {
    	/ *... * /
    })
    return (
    	<div>{count}</div>)}Copy the code
  • useRef

UseRef is used for :(1) only in function components and for manipulating the dom. (2) Equivalent to the global scope, modified in one place, updated everywhere else

Function 1: manipulate dom
const MyInput:React.Fc = (props: IProps) = > {
	const formRef = createRef<HTMLFormElement>()
	const inputRef = useRef<HTMLInputElement>()
    const [ count, setCount ] = useState<number> (0)
    useEffect(() = > {
    	console.log(inputRef)
    	inputRef.current.focus()
    }, [])
    return (
    	<form ref={}>
        	<input type='text' ref={inputRef}/>
        </form>)}// function 2: global scope
const MyInput:React.Fc = (props: IProps) = > {
  const [count, setCount] = useState(0)
  const countRef = useRef(0)
  useEffect(() = > {
      console.log('use effect... ',count)

      const timer = setInterval(() = > {
          console.log('timer... count:', countRef.current)
          setCount(++countRef.current)
      }, 1000)

      return () = > clearInterval(timer)
    },[])

	return (
    	<div>Global scope</div>)}Copy the code

For the global scope, the value of state used in useEffect is fixed inside useEffect and will not be changed unless useEffect refreshes and resets state. so

  • useContext

  • useReducer

  • useCallback

  • useMemo

React’s other simple apis

References:

  • SetState passed the difference between the object and the function: xuexi.erealmsoft.com/2019/09/17/…
  • Why directly modify this. The state is invalid www.jianshu.com/p/1e7e956ec…
  • Life cycle www.jianshu.com/p/b331d0e4b…
  • The state and propshttps: / / juejin. Cn/post / 6844903599235940360
  • React Render optimized: Diff with shouldComponentUpdate juejin.cn/post/684490…
  • React Event mechanism: juejin.cn/post/684490…
  • Key: juejin. Cn/post / 684490…
  • Controlled components and uncontrolled components: www.jianshu.com/p/c4fb11f42…
  • Refs: juejin. Cn/post / 684490…
  • ForwardRef: juejin. Cn/post / 684490…
  • High-level component HOC: juejin.cn/post/684490…
  • The react. Lazy: thoamsy. Making. IO/blogs/react…
  • The react hooks: juejin. Cn/post / 684490…