React

crossorigin

  • Details of a cross-domain error (no write default no details)

  • Console.dir () displays all properties and methods of an object

React.createElement

  • Create a React element, called the virtual DOM, which is essentially an object
  • Parameter 1: Element type, if a string, a normal HTML element
  • Parameter 2: The attribute of the element, an object
  • Subsequent arguments: child nodes of the element

JSX

  • JS extension syntax, need to use Babel to escape.

yarn

Contrast the most common and basic yarn commands with NPM commands

NPM init/YARN init Initialization

Mkdir file name/md file name

NPM install/YARN or YARN install Install dependency

NPM install package –save-dev/yarn add package –dev Installs a dependency

NPM uninstall package –save-dev/yarn remove package –dev Uninstalls a dependency

NPM run dev or NPM start/YARN run start or YARN start runs

The react of scaffolding

  1. Create-react-app scaffolding (YARN create will complete the scaffolding name react-app)
  • yarn create react-app react-learn

  • npm install -g create-react-app

  • create-react-app my-app

  • Start project YARN Start

  1. React files that use JSX must be imported into React.

JSX

  • Each JSX expression requires one more root node, which can be replaced with <>
const h1 = (
  <>
    <div>123</div>
  </>
);

ReactDOM.render(h1, document.getElementById('root'));

/ / which < > < / a > is a syntactic sugar, is < the React. The fragments > < / < React. The fragments >
const a = 1234,
  b = 4321;
const div =
  (
    <div>
      {a} * {b} = {a * b}
    </div>) - the JSX syntax above is equivalent to the react. createElement syntax below;const a = 1234,
  b = 4321;
const div = React.createElement('div', {}, `${a} * ${b} = ${a * b}`);
ReactDOM.render(div, document.getElementById('root'));
Copy the code

What is the JSX

  • Facebook drafted JS extension syntax
  • It is essentially a JS object that will be compiled by bable and eventually converted to createElement
  • Each JSX expression has one and only one root node
  • React.Fragment
  • Each JSX element must end (XML specification)

Embed expressions in JSX

  • Make the expression part of the content
    • Null, and undefined, false, true not be displayed
    • An ordinary object that cannot be a child element
    • We can place React elements (var span = a span element)
    • If it is an array, the array elements are rendered as child elements
  • Treating expressions as element attributes (
  • {i}
  • )

  • Attributes use small camel name (let CLS = “image”
  • {I}
  • // The {} outside of {{}} represents the representation object inside the JS expression

  • A className in JSX is equivalent to a class in the CSS
  • Preventing injection attacks
  • Automatic coding
  • dangerouslySetInnerHTML
// JSX has a protection mechanism by default, and cannot convert strings to HTML code. If you do, add dangerouslySetInnerHTML to the element
const content = '

I Love you very much

Do you like me?

'
; const h1 = ( <li> <div dangerouslySetInnerHTML>{content}</div> </li> ); ReactDOM.render(h1, document.getElementById('root')); DangerouslySetInnerHTML ={{__html:content}} const h1 = ( <li> <div dangerouslySetInnerHTML={{ __html: content,}} ></div> </li> ); Copy the code

Immutability of elements

  • Although a JSX element is an object, all attributes in that object cannot be changed
let num = 0;
const div = <div title="asd">{num}</div>;
div.props.title = 'twd'; // The error attribute cannot be changedIs actuallyObject.freeze(div); // Freeze this property
Copy the code
  • If you do need to change the attributes of an element, you need to recreate the JSX element
setInterval(() = > {
  num++;
  const h1 = <div title="asd">{num}</div>;
  ReactDOM.render(h1, document.getElementById('root'));
}, 1000);
Copy the code

Demo picture switching

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import demo1 from './assets/demo1.jpg';
import demo2 from './assets/demo2.jpg';
import demo3 from './assets/demo3.jpg';
let index = 0;
let arr = [demo1, demo2, demo3]; // console.log(demo1); ///static/media/demo1.e7e78cf4.jpg
const container = document.getElementById('root');
let timer;
function start() {
  stop();
  timer = setInterval(() = > {
    index = (index + 1) % 3;
    const div = <img src={arr[index]} alt=""></img>;
    render(div);
  }, 1000);
}
function render(div) {
  ReactDOM.render(div, container);
}
function stop() {
  clearInterval(timer);
}
start();

container.onmouseenter = function () {
  stop();
};
container.onmouseleave = function () {
  start();
};
Copy the code

Components and component properties

  • Component: UI units that contain content styles and functionality

Creating a component

Special note: The component name must begin with a capital letter

Otherwise, it is not identified as a React component element (that is, it does not make a function call), but as a normal element

  1. Function component

Return a React element

function MyFuncComp(){
    return <h1>The component content</h1>
}
ReactDOM.render(
  <div> { MyFuncComp() }</div>.// The first type (not commonly used, the debugger does not display components, which is not good for debugging)
  <MyFuncComp number="123"></MyFuncComp> // Write it this way
  document.getElementById('root'));Copy the code
  1. Class components

React.ponent must be inherited

import React from 'react';

export default class MyClassComp extends React.Component {
  /** * There must be a render method that returns the React element * this method is on MyClassComp prototype, so you can use this.props */
  render() {
    //return 123;
    return <h1>Class components</h1>; }}Copy the code

You must provide a render function to render the component

Component attributes

  1. For function components, properties are passed to function parameters as properties of an object
<MyFuncComp number="123"></MyFuncComp>;
export default function (props) {
  console.log(props); //{number: "123"}
  return <h1>Component contents {props. Number}</h1>;
}
Copy the code
  1. For class components, the property is passed as an object property to the constructor parameter
ReactDOM.render(
  <div>
    {comp}
    <MyFuncComp number="123"></MyFuncComp>
    <MyClassComp pittle="love" obj={{ name: 'pittle', age: 18}} ></MyClassComp>
    <MyClassComp
      pittle="love"
      obj={{ name: 'pittle', age: 18 }}
      cmp={<h1>I love you</h1>}
    ></MyClassComp>
  </div>.document.getElementById('root'));export default class MyClassComp extends React.Component {
  constructor(props) {
    super(props); // The constructor accepts the parameter props to implement this. Props = props
    / / super (props) equivalent of React.Com ponent. Prototype. Call (this) constructor.
    console.log(this.props, props); //{pittle: "love"}
  }
  /** * This method must return the React element, which is automatically called by React */
  render() {
    //return this.props.pittle;
    return this.props.cmp; }}Copy the code

Note: For component properties, use small hump nomenclature

A component cannot change its own properties. The React element you learned earlier is essentially a component (built-in component).

React philosophy: Who owns the data has the right to change it.

Demo displays the list of students

// Note: await fetch() will return a promise object with the return value result (resp.data) (you can test the above line on the browser)

**index.js**
import React from 'react';
import ReactDOM from 'react-dom';
import StudentList from './components/StudentList';
const appkey = "demo13_1545210570249"
async function FetchData(){
    var stus = await fetch("http://open.duyiedu.com/api/student/findAll?appkey=" + appkey)
    .then(res= > res.json()).then(resp= > resp.data);  / / review the Promise
    return stus;
}
async function render(){
  ReactDOM.render(
    <div>"Loading..."</div>.document.getElementById('root'));const stus = await FetchData();
  ReactDOM.render(
    <div>
       <StudentList stus={stus}></StudentList>
    </div>.document.getElementById('root')); } render(); **Student.js**import React from 'react'
export default function Student(props) {
    // Assume that all student information is transmitted separately from you
    return (
        <li>{/ * all data show a student * /} [name] : {props. The name} [email] : {props. Email} [gender] : {props. Sex = = = 1? 'the man' : 'woman'} [date of birth]:{props. Birth}</li>
    )
}

** StudentList.js **
import React, { Component } from 'react'
import Student from "./Student"
export default class StudentList extends Component {
    render() {
        // the convention props. Stus is passed an array of students
        // Get the component array
        const students = this.props.stus.map(item= > <Student key={item.id} {. item} / >);
        console.log(students);
        return (
            <div>
                {students}
            </div>)}}Copy the code

Component state

Component state: Data that a component can maintain itself

Component state is only valid in class components

A state, essentially a property of a class component, is an object

State initialization

Change of state

Cannot change state directly: React cannot detect state changes

You must use this.setState({}) to change the state

Once called this.setState({}) causes the component to be re-rendered

Data in components

  1. Props: This data is an array that is passed by the component’s consumer. Ownership is not owned by the component itself, so the component cannot change this data
  2. State: This data is created by the component itself and is owned by the component itself, so the component has the right to change this data
ReactDOM.render(
  <div>
    <Tick number={10} />
  </div>.document.getElementById('root'));export default class Tick extends Component {
  // Initialization state: JS Next syntax, currently experimental
  // this.state = {
  // number:this.props.number
  // }
  constructor(props) {
    super(props);
    // Initialization state
    this.state = {
      number: this.props.number,
    };
    this.timer = setInterval(() = > {
      this.setState({
        number: this.state.number - 1});if (this.state.number === 0) {
        clearInterval(this.timer); }},1000);
  }
  render() {
    return <div>Countdown: {this.state.number}</div>; }}Copy the code

The Demo automatically moves the ball

ReactDOM.render(<BallList />.document.getElementById('root'));

import React, { Component } from 'react';

export default class Ball extends Component {
  constructor(props) {
    super(props);
    // Initialization state
    // The horizontal and vertical speed moves in pixels per second
    // props.xSpeed props.ySpeed
    // The background color needs to be passed. If not, red is used
    this.state = {
      left: props.left || 0.top: props.top || 0.xSpeed: props.xSpeed,
      ySpeed: props.ySpeed,
    };
    const duration = 16;
    this.timer = setInterval(() = > {
      const xSpeed = (this.state.xSpeed * duration) / 1000;
      const ySpeed = (this.state.ySpeed * duration) / 1000;
      let newLeft = this.state.left + xSpeed;
      let newTop = this.state.top + ySpeed;
      if (newLeft <= 0) {
        newLeft = 0;
        this.setState({
          xSpeed: -this.state.xSpeed, // The x-coordinate is reversed
        });
      } else if (newLeft >= document.documentElement.clientWidth - 100) {
        newLeft = document.documentElement.clientWidth - 100;
        this.setState({
          xSpeed: -this.state.xSpeed,
        });
      }

      if (newTop <= 0) {
        newTop = 0;
        this.setState({
          ySpeed: -this.state.ySpeed, // the ordinate is reversed
        });
      } else if (newTop >= document.documentElement.clientHeight - 100) {
        newTop = document.documentElement.clientHeight - 100;
        this.setState({
          ySpeed: -this.state.ySpeed,
        });
      }
      this.setState({
        left: newLeft,
        top: newTop,
      });
      if (this.state.number === 0) {
        clearInterval(this.timer);
      }
    }, duration);
  }
  render() {
    return (
      <div
        className="ball"
        style={{
          left: this.state.left.top: this.state.top.background: this.props.bg| | '#f40'}} ></div>); }}Copy the code

The event

In React, the component’s event is essentially a property

Following the React convention for components, since events are essentially properties, you also need to use the small camel name

function handClick() {
  console.log('click');
}
//const BTN = ;
const btn = (
  <button
    onClick={()= >{ console.log('hello world'); }} onMouseEnter={(e) => { console.log(e); }} > click</button>
);

ReactDOM.render(btn, document.getElementById('root'));
Copy the code

In handleClick, this refers to undefined if there is no special handling, and in event handlers, this refers to undefined

// Use the bind function to bind this
constructor(props){
        super(props);
        this.handleClick = this.handleClick.bind(this);
        this.handleOver = this.handleOver.bind(this);
}
handleClick(){}
handleOver(){}
<Tick onClick={this.handleClick.bind(this)} / >// Use arrow functions (handleClick is not on the prototype, but on the object (instance), it is next syntax as before to write state)
handleClick = () = > {}
handleOver = () = > {}
render() {
  return (
  <Tick onClick={this.handleClick()} />)}Copy the code

Making a paging component

Learn more about setState

SetState, which changes state, can be asynchronous (in this case HTML events)

If the code changing state is in an HTML element event, it is asynchronous, otherwise it is synchronous (the timer example above is synchronous)

If you encounter an event that needs to be called multiple times synchronously, you need to use functions to get the latest status

Best practices:

  1. Think of all set states as asynchronous
  2. Never trust the state after a setState call
  3. If you want to use the changed state, you need to use the callback function (the second argument to setState is the callback function)
  4. If the new state is to be evaluated based on the previous state, use a function to change the state (setState first argument is a function).
state = {
  n:0
}
handleClick = () = > {
  this.setState({
    n:this.state.n + 1
  });
  console.log(this.state.n); // Output this.state.n before executing render

  // this.setState({
  // n:this.state.n + 1
  / /}, () = > {
  // // is triggered after a state change and runs after the render function
  // console.log('compelete');
  // });

  // setState is asynchronous and does not change state immediately, so the state is usually 0 each time
  // Because this.state.n is the same every time or 0
  // So all three times it's 0+1
  // this.setState({
  // n:this.state.n + 1
  // });
  // this.setState({
  // n:this.state.n + 1
  // });

  // The following one can be rendered three times, but it is too disgusting
  // this.setState({
  // n:this.state.n + 1
  / /}, () = > {
  // this.setState({
  // n:this.state.n + 1
  //   },() => {
  // this.setState({
  // n:this.state.n + 1
  / /});
  / /});
  // });

  // The first argument to setState can also be a function
  // Three this.setState,cur will save the state of the previous one
  // The source code might put each setState function in a queue, one at a time, when all function calls are made
  // Then change the final state
  this.setState(cur= > { //cur = this.state
    // Cur indicates the current state
    // The result of this function will blend (overwrite) the previous state
    // This function is also executed asynchronously
    return {
      n:cur.n + 1}},() = > {
    // The callback is the same
    // The callback function is executed after all states have been updated and rerendered
   console.log('State update completed'.this.state.n); / / 3
  })
  this.setState(cur= > ({n:cur.n + 1}),() = > {
     // The callback is the same
  })
  this.setState(cur= > ({n:cur.n + 1}),() = > {
     // The callback is the same})}render(){
  console.log('render');
  return (<div>
  <h1>
  {this.state.n}
  </h1>
  <p><button onClick={this.handleClick}>+</button></p>
  </div>)}Copy the code

React optimizes asynchronous setstates and merges multiple setstates (after state changes, render is triggered).

constructor() {
    super(a);this.state = {
      val: 0}; }componentDidMount() {
    this.setState({ val: this.state.val + 1 });
    console.log(this.state.val); // The first result is 0
    this.setState({ val: this.state.val + 1 });
    console.log(this.state.val);// The second result is 0
    setTimeout(() = > {
      console.log(this.state.val); // The third result is 1
      this.setState({ val: this.state.val + 1 });
      console.log(this.state.val);// The fourth result is 2
      this.setState({ val: this.state.val + 1 }); // The fifth result is 3
      console.log(this.state.val);
    }, 0);
  }
  The result is 0, 0, 1, 2, 3
  // This. SetState is triggered once in componentDidMount
  // The setTimeout function is executed synchronously
Copy the code

The life cycle

Life cycle: Components go through a series of processes from birth to destruction. This process is called the life cycle. React provides a set of hook functions (similar to events) throughout the component’s lifecycle, allowing developers to inject code into functions that will run at the appropriate time.

The life cycle exists only in the class component, the function component reruns the function each time it is called, and the old component is immediately destroyed

The old version life cycle

  1. Constructor The same component object is created only once. SetState cannot be called before the first mount to the page. To avoid problems, use setState in constructors is prohibited

  2. ComponentWillMount Normally, like constructors, it is only run once and setState can be used, but to avoid bugs, it is not allowed because the function can be called more than once in some special cases

  3. Render returns a virtual DOM, which will be mounted into the virtual DOM tree and eventually rendered into the real DOM of the page. Render may run more than once and will be re-run whenever it needs to be re-rendered. SetState is strictly prohibited as it may result in infinite recursive rendering

  4. So componentDidMount is only going to execute once and you can use setState and typically, it’s going to write network requests, start timers, whatever you need to do initially, into that function and the component is going to be active

  5. ComponentWillReceiveProps parameter will receive the new attribute value for the new attribute object This function may result in some bug, so don’t recommend use (as long as the attribute to be assignment will trigger, the attribute value is not necessarily change, may be assigned the same two value)

  6. ShouldComponentUpdate specifies whether React wants to rerender the component, by returning true and false to specify that by default, it returns true directly

  7. The componentWillUpdate component is about to be re-rendered

  8. ComponentDidUpdate often uses DOM manipulation in this function to change elements

  9. ComponentWillUnmount usually destroys some component-dependent resources, such as timers, in this function

Note that if the component’s own state is updated, ShouldComponentUpdate, componentWillUpdate Render, and componentDidUpdate DOM are the life cycle methods that are really added to the HTML ComponentDidMount and componentDidUpdate methods. In both of these methods, we get the actual DOM elements. In both of these methods, we get the actual DOM elements. React provides two methods for retrieving DOM elements, one of which is findDOMNode provided by ReactDOM

componentDidMount() {
    console.log(ReactDOM.findDOMNode(this));
  }
Copy the code

React >= 16.0.0 React Officially, there must be a single source of data

  1. So getDerivedStateFromProps is an argument to get new properties and states and that function is static and the return value of that function overrides the component state and that function is almost useless, right
  2. GetSnapshotBeforeUpdate The real DOM is built but not actually rendered into the page. The return value of this function, which is usually used to implement some additional DOM manipulation, is the third argument to componentDidUpdate
export default class OldLifeCycle extends Component {
  constructor(props) {
    super(props);
    this.state = {
      n: 0};console.log('constructor'.'A new component is born!! ');
  }

  componentWillMount() {
    console.log('componentWillMount'.'Component about to be mounted');
  }

  componentDidMount() {
    console.log('componentDidMount'.'Mount complete');
  }

  componentWillReceiveProps(nextProps) {
    console.log(
      'componentWillReceiveProps'.'New attribute value received'.this.props,
      nextProps
    );
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log(
      'shouldComponentUpdate'.'Should I rerender?'.this.props,
      nextProps,
      this.state,
      nextState
    );
    if (this.props.n === nextProps.n && this.state.n === nextState.n) {
      return false;
    }
    return true;
    // return false;
  }

  componentWillUpdate(nextProps, nextState) {
    console.log('componentWillUpdate'.'Component is about to be re-rendered');
  }

  componentDidUpdate(prevProps, prevState) {
    console.log(
      'componentDidUpdate'.'Component has been rerendered',
      prevProps,
      prevState
    );
  }

  componentWillUnmount() {
    console.log('componentWillUnmount'.'Component destroyed');
  }

  render() {
    console.log('render'.'Render, the React element returned will be mounted into the virtual DOM tree');
    return (
      <div>
        <h1>Legacy lifecycle components</h1>
        <h2>Properties of n: {this. Props. N}</h2>
        <h2>State n: {this.state.n}</h2>
        <button
          onClick={()= >{ this.setState({ n: this.state.n + 1, }); }} > Status N +1</button>
      </div>); }}Copy the code

Passing element content

Built-in components: div, H1, P…

<div>asdfafasfafasdfasdf</div>
Copy the code
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Comp from './Comp';
ReactDOM.render(
  <Comp content1={<h2>Group 2 element content</h2>} content2={<h2>Group 3 element content</h2>} ><h2>Group 1 element content</h2>
  </Comp>.document.getElementById('root'));//Comp.js
import React from 'react';
export default function Comp(props) {
  console.log(props);
  return (
    <div className="comp">
      <h1>The content of the component itself</h1>
      {/* {props.children || <h1>The default value</h1>} */}
      {props.children}
      {props.content1}
      {props.content2}
    </div>
  );
}
Copy the code

If you pass element content to a custom component, React passes the element content as the children property.

Make a mask component

// Test.js
import React, { Component } from 'react';
import Model from './common/Model';

export default class Test extends Component {
  state = {
    showModel: false}; showModel =() = > {
    this.setState({
      showModel: true}); }; hideModel =() = > {
    this.setState({
      showModel: false}); };render() {
    return (
      <div>
        <img
          src="data:image/svg+xml; base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xMS41IC0xMC4yMzE3NCAyMyAyMC40NjM0OCI+CiAgPHRpd GxlPlJlYWN0IExvZ288L3RpdGxlPgogIDxjaXJjbGUgY3g9IjAiIGN5PSIwIiByPSIyLjA1IiBmaWxsPSIjNjFkYWZiIi8+CiAgPGcgc3Ryb2tlPSIjNjFkY WZiIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiPgogICAgPGVsbGlwc2Ugcng9IjExIiByeT0iNC4yIi8+CiAgICA8ZWxsaXBzZSByeD0iMTEiIHJ5P SI0LjIiIHRyYW5zZm9ybT0icm90YXRlKDYwKSIvPgogICAgPGVsbGlwc2Ugcng9IjExIiByeT0iNC4yIiB0cmFuc2Zvcm09InJvdGF0ZSgxMjApIi8+CiAgP C9nPgo8L3N2Zz4K"
          alt=""
          width="500"
          height="500"
        />
        {this.state.showModel ? (
          <Model onClose={this.hideModel}>
            <div
              style={{
                background: '#fff'}} >
              <h1>asdfasfasfasfasdfasdf</h1>
              <button onClick={this.hideModel}>The closed meng layer</button>
            </div>
          </Model>
        ) : null}
        <button onClick={this.showModel}>According to meng layer</button>
      </div>); }}// Model.js
import React from 'react';
import './index.css';

export default function Model(props) {
  var defaultProps = {
    // Default properties
    bg: 'rgba(0,0,0,.5)'};var datas = Object.assign({}, defaultProps, props);

  return (
    <div
      onClick={(e)= >{if (e.target.className === 'model') {// Prevent the event from bubbling, onClick datas.onclose (); } }} className="model" style={{ background: datas.bg, }} ><div className="model-center">{datas.children}</div>
    </div>
  );
}
Copy the code

The form

Controlled components and uncontrolled components

Controlled component: A consumer of a component that has full control over the behavior and content of that component. Typically, a controlled component has no state of its own and its content is completely controlled by properties.

Uncontrolled component: A consumer of a component that does not have the ability to control the behavior and content of the component.

Form components, which by default are uncontrolled components that become controlled once the value property of the form component is set (checked for radio and checkboxes)

{/* By default, it is an uncontrolled component */}
{/* <input type="text" /> */}
// Controlled components
<input
  type="text"
  value={this.state.val}
  onChange={(e) = > {
    this.setState({
      val: e.target.value,
    });
  }}
/>

 state = {
    val: '123'.// checked: true
    loves: ['football'.'basketball'.'music'.'other'].chooseLoves: ['basketball'.'music'].selVal: 'beijing'};const checkboxes = this.state.loves.map((it) = > (
      <label key={it}>
        <input
          type="checkbox"
          checked={this.state.chooseLoves.includes(it)}
          onChange={(e)= >{if (e. arget. Checked) {enclosing setState ({chooseLoves: [..... This state chooseLoves, it], / / add}); } else { this.setState({ chooseLoves: this.state.chooseLoves.filter((l) => l ! == it), // remove}); } }} /> {it}</label>
Copy the code

Encapsulating form components

attribute

  1. Datas: An array in which each item is an object corresponding to a checkbox
    1. Object has value and text attributes
    2. Value: indicates the value of multiple boxes
    3. Text: multi-box text

Such as:

datas = [
  { value: 'football'.text: 'football' },
  { value: 'basketball'.text: 'basketball' },
  { value: 'movie'.text: 'movie'},];Copy the code
  1. Name: The name attribute value of each checkbox
  2. Value: indicates the current value
  3. OnChange: The event when the elected median changes

Property defaults and type checking

The React property default is informed by a static property defaultProps

Property type checking uses the library: prop-types

 //commonTypes.js
import PropTypes from "prop-types"

export default {
    children: PropTypes.node,
    groupDatas: PropTypes.arrayOf(PropTypes.shape({
        value: PropTypes.string.isRequired,
        text: PropTypes.string.isRequired
    })), // Data source for multiple box groups, single box groups, and drop-down lists
    chooseDatas: PropTypes.arrayOf(PropTypes.string),
}

import types from ".. /.. /.. /utils/commonTypes"
/** * Default property value */
static defaulProps = {
    datas: [].value: ""
}

static propTypes = {
    datas: types.groupDatas.isRequired,
    name: PropTypes.string.isRequired,
    value: PropTypes.string.isRequired,
    onChange: PropTypes.func
}
Copy the code

Using static propTypes on components tells React how to check properties

PropTypes. Any:// Any typePropTypes. Array:// Array typePropTypes. Bool:// Boolean typePropTypes. Func:// Function typePropTypes. Number:// Number typePropTypes. Object:// Object typePropTypes. String:// A string of charactersPropTypes. Symbol:// Symbol type

a:PropTypes.number.isRequired// Indicates that the a attribute must not be null or undefined
// proptypes. number is actually a function that can be printed with console.dir
/ / PropTypes. Number. IsRequired is also a functionPropTypes. Node:// Anything that can be rendered, strings, numbers, React elementsPropTypes. Element:/ / the react elementsPropTypes. ElementType://react element typeProptypes.instanceof (constructor) :// must be an instance of the specified constructorPropTypes. OneOf ([XXX, XXX]) :/ / the enumeration
PropTypes.oneOfType([xxx, xxx]); // The property type must be one of the arraysPropTypes. ArrayOf (PropTypes. XXX) :// Must be an array of some typePropTypes. ObjectOf (PropTypes. XXX) :// Objects consist of values of a certain typePropTypes. Shape (object) :// The attribute must be an object and satisfy the specified object requirementsPropTypes.exact({... }) :// The object must match exactly the data passed
// Custom attribute check, if there is an error, return the error objectProperties:function(props, propName, componentName) {
/ /...
}
Copy the code

HOC high-level components

HOF: higher-order Function A higher-order Function takes a Function as an argument and returns a Function HOC: higher-order Component Takes a Component as an argument and returns a Component

function Comp(props){
  return <h1>aaaa</h1>
}
class Comp{} <Comp /> React Element <h1></h1> React Html ElementCopy the code

ref

Reference: reference

Scenario: You want to use a method directly from a DOM element, or you want to use a method directly from a custom component

  1. Ref is applied to the built-in HTML component, resulting in a real DOM object
  2. Ref applies to the class component, and the result will be an instance of the class
  3. Ref does not operate on function components
  4. Ref no longer recommends string assignment, which may be removed in the future

string

import React, { Component } from 'react';

class A extends Component {
  method() {
    console.log('Component A's method is called');
  }
  render() {
    return <h1>Component A</h1>; }}// function B(){
// return 

Component B

// } export default class Comp extends Component { handleClick = () = > { console.log(this); this.refs.txt.focus(); this.refs.compA.method(); }; render() { return ( <div> <input ref="txt" type="text" /> <A ref="compA" />{/ *<B ref="compB" />* /}<button onClick={this.handleClick}>Focusing on the</button> </div>); }}Copy the code

Currently, ref recommends objects or functions

object

export default class Comp extends Component {
  constructor(props) {
    super(props);
    this.txt = React.createRef(); This.txt = {current:null}
    // This. TXT = {current:null}
    // react.createref () is simply an efficiency optimization, leaving current unchanged
    // The current attribute is automatically assigned when the render function is executed
  }
  handleclick = () = > {
    console.log(this);
    console.log(this.txt); //{current: input}
    this.txt.current.focus();
  };
  render() {
    return (
      <div>
        <input ref={this.txt} type="text" />
        <button onClick={this.handleclick}>Focusing on the</button>
      </div>); }}Copy the code

Created by the react. createRef function

function

export default class Comp extends Component {
  constructor(props) {
    super(props);
  }
  handleclick = () = > {
    console.log(this);
    this.setState({});
    this.txt.focus();
  };
  componentDidMount() {
    console.log(this.txt, 'didmount');
  }
  render() {
    return (
      <div>
        <input
          ref={(el)= >{// This function is executed at first rendering, and console.log(el, '111') is executed before componentDidMount; // each time this.setState({}) is executed; This is done twice: the first time el is null, and the second time el is the input object this. TXT = el; }} type="text" /><button onClick={this.handleclick}>Focusing on the</button>
      </div>); }}Copy the code
export default class Comp extends Component {
  constructor(props) {
    super(props);
    // this.txt = React.createRef();
  }
  handleclick = () = > {
    console.log(this);
    this.setState({});
    this.txt.focus();
  };
  componentDidMount() {
    console.log(this.txt, 'didmount');
  }
  getRef = (el) = > {
    // The ref does not change, so the render function is executed only once when the render function is executed for the first time
    console.log(el, '111');
    this.txt = el;
  };

  render() {
    return (
      <div>
        <input ref={this.getRef} type="text" />
        <button onClick={this.handleclick}>Focusing on the</button>
      </div>); }}Copy the code

Function call time:

So that’s called for componentDidMount and you can use ref in a componentDidMount event and if ref changes, call the old function and call the new function, The time point is now before componentDidUpdate when the old function is called, passing null when the new function is called, passing object if the component that ref is in is unloaded, calling the function

Be careful with ref

If you can use properties and states for control, don’t use refs.

Calling a method in a real DOM object sometimes requires calling a method on a class component

import React, { Component } from 'react';

export default class Comp extends Component {
  state = {
    show: true}; handleClick =() = > {
    // this.txt.focus();
    this.setState({
      show:!this.state.show,
    });
  };

  componentDidMount() {
    console.log('didMount'.this.txt);
  }

  getRef = (el) = > {
    console.log('Function called', el);
    this.txt = el;
  };

  render() {
    return (
      <div>
        {this.state.show && <input ref={this.getRef} type="text" />}
        <button onClick={this.handleClick}>Show/Hide</button>
      </div>); }}Copy the code

Ref to forward

forwardRef

ForwardRef method:

Parameter, which passes a function component, not a class component, and the function component needs to take a second parameter to get the ref return value, which returns a new component

refs

  • It is important to note that when using DOM native events in React, it is important to remove them manually when the component is uninstalled, otherwise memory leaks may occur.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
export default class NativeEventDemo extends Component {
  componentDidMount() {
    this.refs.button.addEventListener('click'.(e) = > {
      this.handleClick(e);
    });
  }
  handleClick(e) {
    console.log(e);
  }
  componentWillUnmount() {
    this.refs.button.removeEventListener('click');
  }
  render() {
    return <button ref="button">Test</button>; }}Copy the code
import React, { Component } from 'react';
function A(props, ref) {
  return (
    <h1 ref={ref}>Component A<span>{props.word}</span>
    </h1>
  );
}
// Pass function component A to get A new component NewA
const NewA = React.forwardRef(A);
export default class App extends React.Component {
  ARef = React.createRef();
  componentDidMount() {
    console.log(this.ARef);
  }
  render() {
    return (
      <div>
        <NewA ref={this.ARef} word="Props attribute" />
      </div>); }}Copy the code

The class component passes ref through the normal property

1 / / method
class A extends React.Component {
  render() {
    return (
      <h1 ref={this.props.ref1}>Component A<span>{this.props.word}</span>
      </h1>); }}export default class App extends React.Component {
  ARef = React.createRef();
  componentDidMount() {
    console.log(this.ARef);
  }
  render() {
    return (
      <div>
        <A ref1={this.ARef} word="Props attribute" />
      </div>); }}2 / / method
class A extends React.Component {
  render() {
    return (
      <h1 ref={this.props.ref1}>Component A<span>{this.props.word}</span>
      </h1>); }}// Pass function component A to get A new component NewA
const NewA = React.forwardRef((props, ref) = > {
  return <A {. props} ref1={ref} />;
});
export default class App extends React.Component {
  ARef = React.createRef();
  componentDidMount() {
    console.log(this.ARef);
  }
  render() {
    return (
      <div>
        <NewA ref={this.ARef} word="Props attribute" />
      </div>); }}Copy the code

Context

Context: the Context in which something is done

React context features:

When a component creates a context, the data in the context is shared by all descendant components. If a component relies on the context, the component is no longer pure (external data is only sourced from property props). In general, it is used by third-party components (generic components).

The old API

Create context

Only class components can create contexts

Add instance method getChildContext. The object returned by this method is the data in the context. The data must satisfy the type constraint. This method is run after each render. Use data in context

Requirement: To use data in context, the component must have a static contextTypes property that describes the data type in the context to be retrieved

You can get the context data from the component’s context property as a second parameter in the component’s constructor and the data changes in the context

The data in context cannot be changed directly, but is ultimately changed through state

Add a handler function to the context that can be used by descendant components to change the context’s data

import React, { Component } from 'react';
import PropTypes from 'prop-types';

const types = {
  a: PropTypes.number,
  b: PropTypes.string.isRequired,
  onChangeA: PropTypes.func,
};

class ChildA extends Component {
  static contextTypes = types;

  static childContextTypes = {
    a: PropTypes.number,
    c: PropTypes.string,
  };

  getChildContext() {
    return {
      a: 789.c: 'hello'}; }render() {
    return (
      <div>
        <h1>ChildA</h1>
        <h2>A: {enclosing context. A}, b: {enclosing context. B}</h2>
        <ChildB />
      </div>); }}class ChildB extends React.Component {
  /** * Declare which context data is required */
  staticcontextTypes = { ... types,c: PropTypes.string,
  };

  render() {
    return (
      <p>ChildB, data from context: A :{this.context. a}, b:{this.context.b}, c: {this.context.c}<button
          onClick={()= >{ this.context.onChangeA(this.context.a + 2); }} > Button for the child component, A +2</button>
      </p>); }}export default class OldContext extends Component {
  /** * Constrains the type of data in the context */
  static childContextTypes = types;

  state = {
    a: 123.b: 'abc'};/** * get the data in context */
  getChildContext() {
    console.log('Get new Context');
    return {
      a: this.state.a,
      b: this.state.b,
      onChangeA: (newA) = > {
        this.setState({
          a: newA, }); }}; }render() {
    return (
      <div>
        <ChildA />
        <button
          onClick={()= >{ this.setState({ a: this.state.a + 1, }); }} > a plus 1</button>
      </div>); }}Copy the code

A new version of the API

Older apis have serious efficiency issues and are open to abuse

Create context

A context is a component-independent object that is created with React. CreateContext (the default)

An object containing two attributes is returned

Provider property: producer. A component that creates a context with a value property through which you can assign a value to its data

Don’t use the same Provider in multiple components. If you need to use the data in other components, consider elevating the data to the higher level of the Consumer property: Working with data in context, as explained below

In class components, using this.context directly to get context data requires: You must have the static contextType property, which should be the context object created. In the function component, you need to use Consumer to get the context data. Consumer is a child of a component, Is a function (its props. Children need to pass a function) pay attention to detail

If the value property in the Context Provider (context.provider) changes (object-is comparison), all descendant elements provided by that Context will be completely rerendered. Regardless of whether the child element is optimized or not (regardless of what result the shouldComponentUpdate function returns), will it skip shouldComponentUpdate and force rerendering

eg1:

import React, { Component } from 'react';

const ctx = React.createContext();
class ChildB extends React.Component {
  //
  // static contextType = ctx;
  // render() {
  // return (
  // 

/ / a: {enclosing context. A}, b: {enclosing context. B} // // onClick={() => { // this.context.changeA(this.context.a + 2); / /}} // > // Descendant component button, click a+2 // // / /); // } // render() { return ( <ctx.Consumer> {(value) => ( <h1>A: {enclosing context. A}, b: {value. B}<button onClick={()= >{ this.context.changeA(value.a + 2); }} > Descendant component button, click a+2</button> </h1> )} </ctx.Consumer>); }}function ChildA(props) { return ( <div> <h1>childA</h1> <h2> <ctx.Consumer> {(value) => ( <> {value.a},{value.b} </> )} </ctx.Consumer> </h2> <div> <ChildB /> </div> </div> ); } export default class NewContext extends Component { state = { a: 0.b: 'abc'.changeA: (newA) = > { this.setState({ a: newA, }); }};render() { console.log(this.state); return ( <ctx.Provider value={this.state}> <div> <ChildA /> <button onClick={()= >{ this.setState({ a: this.state.a + 1, }); }} > Parent component button, a plus 1</button> </div> </ctx.Provider>); }}Copy the code

Eg2: If CTX is an object, changing ctx.a requires changing the object again.

import React, { Component } from 'react';

const ctx = React.createContext();
class ChildB extends React.Component {
  static contextType = ctx;

  // shouldComponentUpdate(nextProps, nextState) {
  // console.log(' optimized ');
  // return false;
  // }

  render() {
    console.log(this.context, 'context');
    return (
      <h1>A: {enclosing context. A}, b: {enclosing context. B}<button
          onClick={()= >{ this.context.changeA(this.context.a + 2); }} > Descendant component button, click a+2</button>
      </h1>); }}export default class NewContext extends Component {
  state = {
    ctx: {
      a: 0.b: 'abc'.changeA: (newA) = > {
        this.setState({
          ctx: {
            a: newA,
            b: this.state.ctx.b,
            changeA: (newA) = > {
              this.setState({
                ctx: {
                  a: newA,
                  b: this.state.ctx.b,
                  changeA: this.state.ctx.changeA, }, }); ,}}}); ,}}};render() {
    console.log(this.state);
    return (
      <ctx.Provider value={this.state.ctx}>
        <div>
          <ChildB />
          <button
            onClick={()= >{ this.setState({ ctx: { a: this.state.ctx.a + 1, b: this.state.ctx.b, }, }); }} > Parent component button, a plus 1</button>
        </div>
      </ctx.Provider>); }}Copy the code

Eg3: Each time this.setState({}) sets a new Object to state, this new Object inherits the state attribute from the update, and the new attribute overrides the old one, possibly Object.assign(new state, old state). The value property of < ctx.provider value={this.state}> compares references to this.state, so changes in references to state cause context refreshes

import React, { Component } from 'react';

const ctx = React.createContext();

function ChildA(props) {
  return (
    <div>
      <h1>childA</h1>
      <h2>
        <ctx.Consumer>
          {(value) => (
            <>
              {value.a},{value.b}
            </>
          )}
        </ctx.Consumer>
      </h2>
    </div>
  );
}

export default class NewContext extends Component {
  datas = [
    {
      a: 0.b: 'abc'.changeA: (newA) = > {
        this.setState({
          a: newA, }); }},]; state =this.datas[0];

  render() {
    console.log(this.state);
    return (
      <ctx.Provider value={this.state}>
        <div>
          <ChildA />

          <button
            onClick={()= >{ this.setState({}, () => { this.datas.push(this.state); console.log(this.datas[0] === this.datas[1]); }); }} > Parent component button, a plus 1</button>
        </div>
      </ctx.Provider>); }}Copy the code

PureComponent

Pure components to avoid unnecessary rendering (run render function)

Optimization: It is not necessary to re-render a component if its properties and state have not changed

PureComponent is a component whose shouldComponentUpdate is optimized if a component inherits a modifier, comparing properties and states, and not rerendering if they are equal

Equivalent to the following code

shouldComponentUpdate(nextProps, nextState) {
    // Parameters: new state and new props. As prop is re-rendered, new objects are assigned to it
    // shouldComponentUpdate is a function to compare the difference between the new state/props and the old state/props before rerendering
    if (
      ObjectEqual(nextProps, this.props) &&
      ObjectEqual(this.state, nextState)
    ) {
      return false;
    }
    return true;
}

/ / light
// export function ObjectEqual(obj1, obj2) {
// for (let prop in obj1) {
// if (! Object.is(obj1[prop], obj2[prop])) {
// return false;
/ /}
/ /}
// return true;
// }
Copy the code

Pay attention to

  1. PureComponent is shallow and should be used as much as possible for efficiency. PureComponent requires no modification of the previous state, always creating new states to override the previous state (Immutable objects) by having a third party JS Library,Immutable. Js, for making Immutable objects
  2. Function components, using the react. memo function to create a pure component
function Task(props) {
  console.log('Task');
  return <li className={props.isFinish ? 'isFinish' :"'} >{props.name}</li>;
}
Task.propTypes = {
  name: propTypes.string.isRequired,
  isFinish: propTypes.bool.isRequired,
};

export default React.memo(Task);
Copy the code

The React. Memo principle (which is a high-level component)

function memo(FunComp) {
  return class Memo extends PureComponent {
    // render(){
    // return 
      ;
    // }
    render() {
      return <>{FunComp(this.props)}</>; }}; }Copy the code

Note: Avoid this.state.tasks.push(newTask), which adds an item to the previous tasks array and rerenders the previous array. Therefore, the array reference is unchanged if the task is passed as an attribute to the TaskList PureComponent. Since the Tasks reference is unchanged, the TaskList component will not be rerendered

state = {
  tasks: [],}; handleAdd =(newTask) = > {
  this.state.tasks.push(newTask);
  this.setState({
    tasks: this.state.tasks,
  });
};
<TaskList tasks={this.state.tasks} />;
Copy the code
handleAdd = (newTask) = > {
    this.setState({
      tasks: [...this.state.tasks, newTask],
    });
};
<AddTask onAdd={this.handleAdd} />
// The difference is that the onAdd attribute is a different reference each time it is rerendered, while the onAdd attribute is the same reference each time
<AddTask onAdd={(newTask)= >{ this.setState({ tasks: [...this.state.tasks, newTask], }); }} / >

Copy the code

render props

In some cases, the various functions and processing logic of some components are almost identical, but the interface displayed is different. It is recommended to solve the problem of duplicate code in either of the following ways (crosscutting concerns)

  1. This property is a function, and the return value of the function used to render the parameters of the function will be passed as the data needed. Note the pure component property (try not to pass the address of the render props inconsistent each time), usually the name of this property is render
  2. HOC
import MouseListener from './MouseListener';
import React from 'react';

const renderPoint = (mouse) = > (
  <>X}, y} {mouse.</>
);
const renderDiv = (mouse) = > (
  <>
    <div
      style={{
        width: 100.height: 100.background: '#008c8c',
        position: 'absolute',
        left: mouse.x - 50.top: mouse.y - 50,}} ></div>
  </>
);

export default function Test() {
  return (
    <div>
      <MouseListener render={renderPoint} />
      <MouseListener render={renderDiv} />
    </div>
  );
}
Copy the code
import React, { PureComponent } from 'react';
import './style.css';

/** * This component listens for mouse changes */
export default class MouseListener extends PureComponent {
  state = {
    x: 0.y: 0}; divRef = React.createRef(); handleMouseMove =(e) = > {
    // Update x and y values
    const { left, top } = this.divRef.current.getBoundingClientRect();
    const x = e.clientX - left;
    const y = e.clientY - top;
    this.setState({
      x,
      y,
    });
  };

  render() {
    return (
      <div
        ref={this.divRef}
        className="point"
        onMouseMove={this.handleMouseMove}
      >{this.props.render ? This.props. Render (this.state) : 'default '}</div>); }}Copy the code

Portals

Slot: Renders a React element into the specified DOM container

Reactdom.createportal (React element, real DOM container), which returns a React element

import React from 'react';
import ReactDOM from 'react-dom';

function ChildA() {
  return ReactDOM.createPortal(
    <div
      className="child-a"
      style={{
        marginTop: 200,}} >
      <h1>ChildA</h1>
      <ChildB />
    </div>.document.querySelector('.modal')); }function ChildB() {
  return (
    <div className="child-b">
      <h1>ChildB</h1>
    </div>
  );
}

export default function App() {
  return (
    <div
      className="app"
      onClick={(e)= >{console.log('App was clicked ', e.target); }} ><h1>App</h1>
      <ChildA />
    </div>
  );
}
Copy the code

Notice that events bubble. Events in React are wrapped and bubble up based on the virtual DOM tree, not the real DOM tree.

Error boundary

By default, a component error during render causes the entire component tree to be uninstalled

Error boundary: a component that catches errors that occur in child components during render and has the ability to prevent further propagation of errors

Have a component catch errors

GetDerivedStateFromError (DOM has not yet been updated) Static function runtime point: When rendering a child component, after an error occurs, before updating the page note: React returns an object whose property overrides the state argument of the current component: Error object Normally, this function is used to change state

ComponentDidCatch (DOM has already been updated) In general, this function is used to log error messages (this function is used to unload all components and then rebuild the component to re-render).

GetDerivedStateFromError: Renders alternate UI componentDidCatch: prints error messages

import React, { PureComponent } from 'react';

export default class ErrorBound extends PureComponent {
  state = {
    hasError: false};static getDerivedStateFromError(error) {
    console.log('An error has occurred', error);
    return {
      hasError: true}; }// componentDidCatch(error, info) {
  // console.log(' Record error information ');
  // }

  render() {
    // setTimeout(() => {
    // throw new Error("asfasdfasf");
    // }, 1000);
    if (this.state.hasError) {
      return <h1>There was an error!</h1>;
    }
    return this.props.children; }}Copy the code

details

Some errors that the error boundary component cannot catch

Errors in asynchronous error events Server render Summary: Only handle synchronization errors during rendering child components

Events in React

Events here: events in the DOM component built into React

Almost all of the event handling for the document element, it handles some non-bubbling event in the document event, it listens directly on the element for some event that’s not on the document, it listens directly on the element for the event handling on the document, The React event parameter is not a real DOM event parameter. The React object is similar to the event parameter stopPropagation in the real DOM. To improve execution efficiency, React uses a pool of event objects to process event objects

import React from 'react';
export default function App() {
  return (
    <div>
      <button
        onClick={(e)= >Console. log('react1: the button was clicked ', e.ativeEvent); e.nativeEvent.stopPropagation(); }} > button</button>
    </div>
  );
}

document.querySelector('#root').onclick = function (e) {
  console.log('root: Prevent onclick event from bubbling ');
  e.stopPropagation();
};

document.onclick = function (e) {
  console.log('Document: Prevent onClick event from bubbling');
  // e.stopPropagation();
};
Copy the code

Matters needing attention

If you register an event with a real DOM to prevent the event from bubbling, the React event will not fire. If you register an event with a real DOM, the event will run before the React event. Don’t stop real DOM event bubbling through nativeEvent. StopImmediatePropagation (), stop the execution of the remaining events in the history of the document In the event handler, do not use asynchronous event object, if must use, The persist function needs to be called

import React from 'react';
var prev;
export default function App() {
  return (
    <div
      onClick={(e)= >{ console.log(prev === e); Console. log('react: div was clicked '); }} ><input
        type="text"
        onFocus={(e)= >{console.log(' React: text gets focus '); }} / ><button
        onClick={(e)= >{console.log(' React: the button was clicked '); prev = e; e.persist(); setTimeout(() => { console.log(e.type); }, 1000); // e.nativeEvent.stopImmediatePropagation() // console.log(e.isPropagationStopped()); // e.stopPropagation(); // console.log(e.isPropagationStopped()); }} > button</button>
    </div>
  );
}

document.querySelector('#root').onFocus = function (e) {
  console.log('Prevent focus events from bubbling');
  e.stopPropagation();
};
Copy the code

Rendering process

Render: Generate objects for display and form these objects into real DOM objects

  • React Element: React Element, created with React. CreateElement (syntax sugar: JSX) example:
<div><h1>The title</h1></div>
<App />
Copy the code
  • React node: Objects that are specifically used for rendering to the UI. React elements are used to create React nodes. The ReactDOM must be rendered using React nodes

  • The React element type used to create the node is a string (“h1″,” P “). The React element type used to create the node is either a function or a text node. React array node: This node is created from an array. Real DOM: the DOM element created with document.createElement

import React from 'react';
class CompA extends React.Component {
  state = {
    a: 123.b: 'abc'};componentDidUpdate(prevProps, prevState) {
    console.log('CompA', prevProps, prevState, this.state);
    console.log('CompA componentDidUpdate');
  }

  render() {
    return (
      <div>
        <h1>{this.state.a}</h1>
        <CompB n={this.state.b} />
        <button
          onClick={()= >{ this.setState({ a: 321, b: 'cba', }); }} > click</button>
      </div>); }}function CompB(props) {
  return (
    <div>
      <h1 id="title">{props.n}</h1>
      <CompC n={props.n} />
    </div>
  );
}

class CompC extends React.Component {
  componentDidUpdate(prevProps, prevState) {
    console.log('CompC', prevProps, prevState, this.props);
    console.log('CompC componentDidUpdate');
  }

  render() {
    var title = document.getElementById('title');
    if (title) {
      console.log(title.innerHTML);
    } else {
      console.log(title);
    }
    return (
      <div>
        <h1>{this.props.n}</h1>
      </div>); }}export default class App extends React.Component {
  render() {
    return (
      <div>
        <CompA />
      </div>); }}Copy the code

First render (new node render)

  1. Creates a node from the value of the parameter
  2. Do different things for different nodes
  • Text node: Create a real text node with document.createTextNode
  • Empty node: do nothing
  • Array nodes: Iterate through the array, recursively creating nodes for each item in the array (go back to Step 1 and repeat until the iteration is complete)
  • DOM node: Create a real DOM object with document.createElement, and immediately set the properties of the real DOM element. Then iterate over the Children property of the React element recursively (go back to Step 1 and repeat until the loop is complete).
  • Component node a. Function component: call the function (which must return a content that can generate a node), recursively generate the node with the return result of the function (go back to step 1 and repeat the operation until the end of the traversal) B. Types of components:
    • Create an instance of the class
    • Call the lifecycle method of the object immediately: static getDerivedStateFromProps
    • Run the Render method on that object to get the node object (recurse the node, go back to Step 1 and repeat)
    • The component’s componentDidMount is added to an execution queue (fifO, fifO, fifO) that executes when the entire virtual DOM tree has been built and real DOM objects have been added to the container
  1. Once you have generated the virtual DOM tree, save it for later use
  2. Add the actual DOM object you generated earlier to the container.

Update the node

Updated scenarios:

Calling setState in the instance object of the class component causes the node on which the instance is located to update the node

If reactdom. render is called, enter the diff update of the root node. If setState is called, run the lifecycle function, Static getDerivedStateFromProps shouldComponentUpdate, if this function returns false, abort the current process and run Render to get a new node, Add the life cycle function getSnapshotBeforeUpdate to the queue for future execution Add the life cycle function componentDidUpdate to the queue for future execution:

Update the virtual DOM tree to complete the real DOM update call componentDidMount call getSnapshotBeforeUpdate call componentDidUpdate in the execution queue

Compared to update

Compare the newly generated node with the node in the previous virtual DOM to find the difference and complete the update

Question: Compare which node in the DOM tree before

React makes the following assumptions to improve comparison efficiency

  1. It is assumed that there will be no hierarchical movement of nodes (for comparison, directly find nodes at corresponding positions in the old tree for comparison)

  2. Different types of nodes generate different types of nodes with the same structure: The nodes themselves are of the same type. If generated by the React element, the type value must also be the same as the others, which belong to different node types

  3. Multiple brothers identify the new node for comparison by a unique key

    Functions of the key value: It is used to find the corresponding new node through the old node. If an old node has a key value, it will find nodes with the same key value in the same hierarchy for comparison when it is updated.

The key value should be unique in a range (among sibling nodes) and should remain stable

Find a target for comparison

Check whether the node types are consistent

Consistently do different things for different node types

Empty node: does nothing

DOM node:

Directly reuse the previous real DOM object to record the changes in its properties, waiting for future updates to be completed uniformly (no real changes now) to traverse the children of the new React element, recursively comparing updates

Text node:

Directly reuse the previous real DOM object to record the new text changes for future updates

Component node:

Function component: re-call the function, get a node object, enter the recursive comparison update

Types of components:

Call shouldComponentUpdate. If this method returns false, abort render and get a new node object. Add getSnapshotBeforeUpdate of the object to the queue Add componentDidUpdate of the object to the queue array node: Iterate over the number group for recursive comparison update

Inconsistent Overall, uninstall the old node and create a new node

Creating a New node

The process for mounting the new node is displayed

Uninstalling the old node

Text node, DOM node, array node, empty node, function component node: directly abandon the node, if the node has child node, recursively unload node class component node: directly abandon the node call the node’s componentWillUnMount function recursively unload the child node

import React, { Component } from 'react';

class CompA extends React.Component {
  state = {
    a: 123.b: 'abc'};componentDidUpdate(prevProps, prevState) {
    console.log('CompA componentDidUpdate');
  }

  render() {
    return (
      <div>
        <h1>{this.state.a}</h1>
        <CompB n={this.state.b} />
        <button
          onClick={()= >{ this.setState({ a: 321, b: 'cba', }); }} > click</button>
      </div>); }}function CompB(props) {
  return (
    <div>
      <h1 id="title">{props.n}</h1>
      <CompC n={props.n} />
    </div>
  );
}

class CompC extends React.Component {
  componentDidUpdate(prevProps, prevState) {
    console.log('CompC componentDidUpdate');
  }

  render() {
    var title = document.getElementById('title');
    if (title) {
      console.log(title.innerHTML);
    } else {
      console.log(title);
    }
    return (
      <div>
        <h1>{this.props.n}</h1>
      </div>); }}export default class App extends Component {
  render() {
    return (
      <div>
        <CompA />
      </div>); }}Copy the code
There is no target for comparison

Nodes in the new DOM tree have been deleted

Nodes are added to the new DOM tree

Create new nodes to uninstall redundant old nodes

tool

Strict mode

StrictMode(react. StrictMode) is a component that does not render UI (react. Fragment <> </>).StrictMode is a component that does not render UI (react. Fragment <> </>).

Warning about using the outdated string REF API Warning about using the deprecated findDOMNode method Detect unexpected side effects React requires, ComponentDidMount ComponentDidUpdate ComponentWillUnMount Side effects: A function that does something that affects data outside the function, such as:

Asynchronously handling changes to parameter values setState Locally stores changes to variables outside the function Conversely, a function can be considered a pure function if it has no side effects

In strict mode, it does not monitor specific side effect code, but it calls functions that do not have side effects twice in order to detect problems. (This is only valid in development mode)

Detect stale context APIS

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>.document.getElementById('root'));Copy the code

Profiler

Performance analysis tool

Analyze the render times of components involved in one or more commits (updates)

Flame map: Gets the total render time for each component and its own render time for a commit

Sort diagram: Get the order of each component’s own rendering time for a particular commit

Component diagram: How long it takes for a component to render itself over multiple commits

HOOK profile

HOOK came after Act 16.8.0

Components: stateless components (function components), class components

Trouble with class components:

This points to the problem

Tedious life cycle

Other problems

Hooks are specifically designed to enhance the functionality of functional components (hooks are not available in class components), making them theoretically possible substitutes for class components

It’s official: there is no need to change the class components that have been completed. There are no plans to eliminate class components, only to encourage the use of functional components

A HOOK is essentially a function (always named starting with use) that can mount any function

HOOK type: useState useEffect

State Hook

A State Hook is a function (useState) used in a function component to useState in a function component

useState

  1. UseState is called the NTH time
  2. Check whether the node has a subscript N in its state array
  3. There is no way to create a state with default values and add the state to the state array with subscript N
  4. The default value is ignored and the status value is obtained
import React, { useState } from 'react';

export default function App() {
  / / USES shortcuts
  const [n, setN] = useState(0);
  return (
    <div>
      <button
        onClick={()= >{ setN(n - 1); > -}}</button>
      <span>{n}</span>
      <button
        onClick={()= >{ setN(n + 1); > +}}</button>
    </div>
  );
}
Copy the code

Function with one parameter, the value of this parameter according to the state of the default function of the return value is an array, the array must contain two paragraph: value of the current state of the second: the change of state function, call this function will lead to rendering) a function component can have multiple state, it is very beneficial to horizontal segmentation concerns.

Attention to Detail

  1. UseState is best written at the start of the function for easy reading
  2. UseState is strictly prohibited in code blocks (judgments, loops)
  3. Function returned by useState (second item in array), reference invariant (memory saving)
import React, { useState } from 'react';

window.arr = [];

export default function App() {
  const [visible, setVisible] = useState(true);
  const [n, setN] = useState(0);
  window.arr.push(setN); // Compare arr[0] === arr[1] on the browser to true
  return (
    <div>
      <p style={{ display: visible ? 'block' : 'none' }}>
        <button
          onClick={()= >{ setN(n - 1); > -}}</button>
        <span>{n}</span>
        <button
          onClick={()= >{ setN(n + 1); > +}}</button>
      </p>
      <button
        onClick={()= >{ setVisible(! visible); }} > Show/Hide</button>
    </div>
  );
}
Copy the code
  1. Using functions to change the data, if the data is exactly the same as the previous data (using object.is comparison), does not cause re-rendering, for optimization purposes.
import React, { useState } from 'react';

export default function App() {
  console.log('App Render');
  const [visible, setVisible] = useState(true);
  const [n, setN] = useState(0);
  return (
    <div>
      <p style={{ display: visible ? 'block' : 'none' }}>
        <button
          onClick={()= >{ setN(n - 1); > -}}</button>
        <span>{n}</span>
        <button
          onClick={()= >{ setN(n + 1); > +}}</button>
      </p>
      <button
        onClick={()= >{ // setVisible(! visible); // setVisible(visible) will be rerendered; }} > show/hide will not be rerendered without changing visible</button>
    </div>
  );
}
Copy the code
  1. Using functions to change data, the value passed in is not merged with the original data, but replaced directly.
import React, { useState } from 'react';

export default function App() {
  console.log('App Render');
  const [data, setData] = useState({
    x: 1.y: 2});return (
    <div>
      <p>X: {data.x}, y: {data.y}<button
          onClick={()= >{ setData({ ... data, x: data.x + 1, }); / / below this kind of writing will place 2} {x: 1, y: directly replace {x: data. X + 1} / / setData ({/ / x: data. X + 1 / /})}} > x + 1</button>
      </p>
    </div>
  );
}
Copy the code
  1. If you want to implement a force refresh component class component: use the forceUpdate function
import React, { Component } from 'react';
export default class App extends Component {
  render() {
    return (
      <div>
        <button
          onClick={()= >{shouldComponentUpdate this.forceUpdate(); // Force rerender}} > Force refresh</button>
      </div>); }}Copy the code

Function component: useState with an empty object

import React, { useState } from 'react';
export default function App() {
  console.log('App Render');
  const [, forceUpdate] = useState({});
  return (
    <div>
      <p>
        <button
          onClick={()= >{ forceUpdate({}); }} > Force refresh</button>
      </p>
    </div>
  );
}
Copy the code
  1. If some states are not necessarily related to each other, they should be split into different states rather than merged into one object

  2. As with the state of a class component, changing state in a function component can be asynchronous (in the case of DOM events), where multiple state changes are merged for efficiency, the previous state cannot be trusted and should be changed using a callback function. If the state changes to use the previous state, try to pass the function.

import React, { useState } from 'react';
export default function App() {
  console.log('App render');
  const [n, setN] = useState(0); // Use a state whose default value is 0
  return (
    <div>
      <button
        onClick={()= >{ // setN(n - 1); // setN(n - 1); setN((prevN) => prevN - 1); SetN ((prevN) => prevn-1); > -}}</button>
      <span>{n}</span>
      <button
        onClick={()= >SetN (prevN) => prevN + 1; prevN (prevN + 1) => prevN + 1; SetN ((prevN) => prevN + 1); // Each function is executed in turn so the result is 2}} > +</button>
    </div>
  );
}
Copy the code

Effect Hook

Effect Hook: Used to handle side effects in function components

Side effects:

Ajax request timers other asynchronous operations that change the local storage of real DOM objects other operations that have external effects: useEffect, which takes a function as an argument to which side effects are required

The following example shows that the useEffect component does not execute when uninstalling the function, but the cleanup function does

import React, { useState, useEffect } from 'react';
const ref = React.createRef();
window.timer = null; // Timer ID

function stop() {
  clearInterval(window.timer); // Clear the previous timer
  window.timer = null;
}
/** * a moveable block that always moves from 0,0 to the target point * within 10 seconds after each rendering@param {*} props* props. Left, y-coordinate of the target point to be moved */
function MovableBlock(props) {
  useEffect(() = > {
    console.log('render');
    // After rendering is complete
    const div = ref.current;
    let curTimes = 0; // The number of current moves
    const disX = props.left / 1000; // The distance of each move on the x-coordinate
    const disY = props.top / 1000; // The distance of each move on the y-coordinate
    window.timer = setInterval(() = > {
      curTimes++; // Number of moves +1
      const newLeft = curTimes * disX;
      const newTop = curTimes * disY;
      div.style.left = newLeft + 'px';
      div.style.top = newTop + 'px';
      if (curTimes === 1000) { stop(); }},10);
    return stop; // Use stop directly as the cleanup function
  });

  return (
    <div
      ref={ref}
      style={{
        width: 100.height: 100.left: 0.top: 0.position: 'fixed',
        background: '#f40'}} ></div>
  );
}

export default class App extends React.Component {
  state = { x: 0.y: 0.visible: true };
  render() {
    console.log('App render');
    return (
      <div
        style={{
          paddingTop: 200,}} >
        {this.state.visible && (
          <div>
            x:{' '}
            <input
              type="number"
              value={this.state.x}
              onChange={(e)= > {
                this.setState({
                  x: parseInt(e.target.value),
                });
              }}
            />
            y: <input
              type="number"
              value={this.state.y}
              onChange={(e)= >{ this.setState({ y: parseInt(e.target.value), }); }} / ><MovableBlock left={this.state.x} top={this.state.y} />
          </div>
        )}

        <button
          onClick={()= >{ this.setState({ visible: ! this.state.visible, }); console.log(window.timer); }} > Show/Hide</button>
      </div>); }}Copy the code

UseEffect () specifies a side effect function that is automatically executed every time a component is rendered. The side effects function is also executed after the component is first loaded in the WEB DOM. If the second argument is an empty array, then the side effect argument does not have any dependencies. Therefore, the side effects function is executed only once after the component is loaded into the DOM, and not again after the component is re-rendered. This makes sense, because side effects do not depend on any variables, so no matter how those variables change, the result of the side effect function will not change, so run once is enough.

details

  1. The side effect function runs at a point in time after the actual UI rendering of the page is complete. So it executes asynchronously and doesn’t block the difference between componentDidMount and componentDidUpdate in the browser and the class component, changing the real DOM, But the user hasn’t seen the UI update yet, synchronized. UseEffect is a side effect function that changes the real DOM, and the user already sees UI updates, asynchronously.
  2. UseEffect can be used multiple times per function component, but not in blocks of code such as judgments or loops.
  3. UseEffect is a side effect function that can have a return value, and the return value must be a function called a cleanup function that runs at a point in time before the first rendering of the side effect function does not run and must run when the component is destroyed
  4. UseEffect is a function that passes a second parameter which is an array of dependencies that record the side effect and when the component is rerendered, the side effect will only be executed if the dependency is different from the last one so when you pass the dependency, If the data has not changed, the side effects function only runs after the first render and the cleanup function only runs after the component is unloaded

In practice, since side effects are executed every render by default, the cleanup function is executed not only once when the component is unloaded, but also once every time the side effects function is re-executed to clean up the side effects of the previous render.

import React, { useState, useEffect } from 'react';

function Test() {
  useEffect(() = > {
    console.log('Side effect function, run only once at mount time');
    return () = > {
      console.log('Clean up function, run once only at unload time'); }; } []);// With an empty array as a dependency, the side effect function is run only at mount time, and useEffect is executed on every render if the second argument is not written
  console.log('Render component');
  const [, forceUpdate] = useState({});

  return (
    <h1>Test component {' '}<button
        onClick={()= >{ forceUpdate({}); }} > Refresh the component</button>
    </h1>
  );
}

export default function App() {
  const [visible, setVisible] = useState(true);
  return (
    <div>
      {visible && <Test />}
      <button
        onClick={()= >{ setVisible(! visible); }} > Show/Hide</button>
    </div>
  );
}
Copy the code
  1. In a side effect function, variables in the function context will not change in real time due to closures.
import React, { useState, useEffect } from 'react';

export default function App() {
  const [n, setN] = useState(0);
  useEffect(() = > {
    setTimeout(() = > {
      console.log(n); //n refers to n when the current App function is called
    }, 5000);
  });
  return (
    <div>
      <h1>{n}</h1>
      <button
        onClick={()= > {
          setN(n + 1);
        }}
      >
        n+1
      </button>
    </div>
  );
}
Copy the code

Error writing

import React, { useState, useEffect } from 'react';
// The following is the wrong approach
export default function App() {
  const [n, setN] = useState(10);
  useEffect(() = > {
    // Run only after mounting
    const timer = setInterval(() = > {
      const newN = n - 1;
      console.log(newN); // This will print 9 all the time, which means that n is not real time n, and that n is the closure 10 every time
      setN(newN);
      if (newN === 0) {
        clearInterval(timer); }},1000);
    return () = > {
      // Run when the function is unloaded
      clearInterval(timer); }; } []);// If there is no dependency, it will be executed only once
  return (
    <div>
      <h1>{n}</h1>
      <button
        onClick={()= > {
          setN(n + 1);
        }}
      >
        n+1
      </button>
    </div>
  );
}
Copy the code

Correct term

import React, { useState, useEffect } from 'react';
let timer;
export default function App() {
  const [n, setN] = useState(10);
  useEffect(() = > {
    if (n === 0) {
      return;
    }
    // Once a render is complete, it needs to be rerendered 1 second later according to the current value of n
    timer = setTimeout(() = > {
      setN(n - 1);
    }, 1000);
    return () = > {
      clearTimeout(timer);
    };
  }, [n]);
  return (
    <div>
      <h1>{n}</h1>
      <button
        onClick={()= > {
          setN(n + 1);
        }}
      >
        n+1
      </button>
    </div>
  );
}
Copy the code
  1. At each registration, the side effect function overwrites the previous side effect function. Therefore, try to keep the side effect function stable, otherwise it will be complicated to control.
import React, { useState, useEffect } from 'react';

let n = 1;

function func1() {
  console.log('Odd side effect function');
  return () = > {
    console.log('Odd cleanup function');
  };
}

function func2() {
  console.log('Even side effect function');
  return () = > {
    console.log('Even cleanup function');
  };
}

export default function App() {
  const [, forceUpdate] = useState({});
  useEffect(n % 2= = =0 ? func2 : func1);
  n++;
  return (
    <div>
      <button
        onClick={()= >{ forceUpdate({}); }} > Force refresh</button>
    </div>
  );
}
Copy the code

Customize the HOOK

State Hook: useState Effect Hook: useEffect

Custom Hook: Some commonly used Hook functions that span multiple components are extracted and formed into a function. This function is called custom Hook. Since it needs to use Hook functions internally, it also needs to be implemented in accordance with Hook rules:

  1. The function name must start with use
  2. Custom Hook functions should be called at the top level

Such as:

  1. Many components need to get all the student data after the first load
// App.js
import React from 'react';
import useAllStudents from './components/common/CustomizeUse/useAllStudents';
function Test() {
  const stus = useAllStudents();
  const list = stus.map((it) = > <li key={it.id}>{it.name}</li>);
  return <ul>{list}</ul>;
}
export default function App() {
  return (
    <div>
      <Test />
    </div>
  );
}
// useAllStudents.js
import { useEffect, useState } from 'react';
import { getAllStudents } from '.. /.. /service/index';
/** * Get all student data */ when the component is loaded for the first time
export default function useAllStudents() {
  const [students, setStudents] = useState([]);
  useEffect(() = >{(async() = > {const stus = await getAllStudents();
      setStudents(stus);
    })();
  }, []);
  return students;
}
Copy the code
  1. Many components need to start a timer after the first load and then uninstall the Hook on component destruction. Eslint has a plugin (eslint-plugin-react-hooks) that issues warnings if the Hook rules are not strictly followed/* eslint "react-hooks/exhaustive-deps": "off" */
// App.js
import React, { useState } from 'react';
import useTimer from './components/common/CustomizeUse/useTimer';
function Test(props) {
  useTimer(() = > {
    console.log('Some side effects of the Test component');
  }, 1000);
  return <h1>The Test component</h1>;
}

export default function App() {
  const [visible, setVisible] = useState(true);
  return (
    <div>
      {visible && <Test />}
      <button
        onClick={()= >{ setVisible(! visible); }} > Hide/show</button>
    </div>
  );
}

//useTimer.js
/* eslint "react-hooks/exhaustive-deps": "off" */
import { useEffect } from 'react';
/** * An Interval timer is started when the component is first rendered. * This timer is cleared when the component is uninstalled
export default (func, duration) => {
  useEffect(() = > {
    const timer = setInterval(func, duration);
    return () = > {
      clearInterval(timer); }; } []); };Copy the code
  1. Leverage higher-order components
import React from 'react';
import { getAllStudents } from './components/service/index';

function withAllStudents(Comp) {
  return class AllStudentsWrapper extends React.Component {
    state = {
      stus: [],};async componentDidMount() {
      const stus = await getAllStudents();
      this.setState({
        stus,
      });
    }

    render() {
      return <Comp {. this.props} stus={this.state.stus} />; }}; }function Test(props) {
  const list = props.stus.map((it) = > <li key={it.id}>{it.name}</li>);
  return <ul>{list}</ul>;
}

const TestStudents = withAllStudents(Test);

export default function App() {
  return (
    <div>
      <TestStudents />
    </div>
  );
}
Copy the code
  1. Using the prop render
import React from 'react';
import { getAllStudents } from './components/service/index';

class AllStudents extends React.Component {
  state = {
    stus: [],};async componentDidMount() {
    const stus = await getAllStudents();
    this.setState({
      stus,
    });
  }

  render() {
    if (typeof this.props.render === 'function') {
      return this.props.render(this.state.stus);
    }
    return null; }}function Test(props) {
  const list = props.stus.map((it) = > <li key={it.id}>{it.name}</li>);
  return <ul>{list}</ul>;
}

export default function App() {
  return (
    <div>
      <AllStudents render={(stus)= > <Test stus={stus} />} / ></div>
  );
}
Copy the code
  1. Practice usePageStudent
//App.js
import React, { useState } from 'react';
import usePageStudents from './components/common/CustomizeUse/usePageStudents';

function Test() {
  const [page, setPage] = useState(1);
  const resp = usePageStudents(page, 10);
  if (resp) {
    const list = resp.findByPage.map((it) = > <li key={it.id}>{it.name}</li>);
    return (
      <div>
        <h1>Data count: {resp.cont}</h1>
        <ul>{list}</ul>
        <input
          type="number"
          value={page}
          onChange={(e)= >{ setPage(parseInt(e.target.value)); }} / ></div>
    );
  }
  return null;
}

export default function App() {
  return (
    <div>
      <Test />
    </div>
  );
}

// usePageStudent.js
import { useEffect, useState } from 'react';
import { getStudents } from '.. /.. /service/index';
/** * Get student data based on page number and page size, get a response * and get student data again when page number and page size change */
export default function useAllStudents(page = 1, limit = 10) {
  const [resp, setResp] = useState();
  useEffect(() = >{(async() = > {const resp = awaitgetStudents(page, limit); setResp(resp); }) (); }, [page, limit]);return resp;
}
Copy the code

Reducer Hook

Reducer Hook Flux: A data flow framework created by Facebook

  1. It is stipulated that data flows in one direction
  2. Data is stored in a data warehouse (for now, you can think of state as a warehouse where data is stored)
  3. An action is the only reason for changing data. It’s essentially an object. It has two properties. }} For example, if you want to delete a student, the action can be described as: {type:”deleteStudent”, payload: student ID}
  4. What changes the data is a function called Reducer which receives two parameters state: action in the current data warehouse: This function must have a return result, which is used to represent the data Flux requirements after the data warehouse changes. The object is immutable. If the object is returned, a new object must be created. If the reducer is to be triggered, the reducer should not be called directly, but an auxiliary function dispatch should be called. This function receives only one parameter: action. This function will call the Reducer indirectly to change the data
window.arr = [];

import { useState } from 'react';
/** * The generic useReducer function *@param {function} Reducer Reducer function, standard format *@param {any} InitialState initialState *@param {function} InitFunc The function used to calculate the initial value */
export default function useReducer(reducer, initialState, initFunc) {
  const [state, setState] = useState(
    initFunc ? initFunc(initialState) : initialState
  );

  function dispatch(action) {
    const newState = reducer(state, action);
    console.log(Log: the value of n${state}->${newState}`);
    setState(newState);
  }

  return [state, dispatch];
}
Copy the code
// import React, { useReducer } from 'react'
import React from 'react';
import useReducer from './components/other/useReducer';
/** * This function, based on the current data, has been action, generates a new data *@param {*} state
 * @param {*} action* /
function reducer(state, action) {
  switch (action.type) {
    case 'increase':
      return state + 1;
    case 'decrease':
      if (state === 0) {
        return 0;
      }
      return state - 1;
    default:
      returnstate; }}export default function App() {
  console.log('Re-render'); // each time in useReducer setState(newState); Will directly cause the App function to be re-executed and re-rendered (i.e. components that rely on State Hook will be re-rendered).
  const [n, dispatch] = useReducer(reducer, 10.(args) = > {
    console.log(args); // The third argument is a function that takes useReducer's second argument as an argument
    return 100;
  });
  //window.arr.push(dispatch); //arr[0]===arr[1] results in false, indicating that the dispatch after each re-rendering is not the same
  return (
    <div>
      <button
        onClick={()= >{ dispatch({ type: 'decrease' }); > -}}</button>
      <span>{n}</span>
      <button
        onClick={()= >{ dispatch({ type: 'increase' }); > +}}</button>
    </div>
  );
}
Copy the code

Context Hook

Used to get context data

import React, { useContext } from 'react';

const ctx = React.createContext();

// function Test() {
// return 
      
// {value => 

Test, context value: {value}

}
// // } function Test() { const value = useContext(ctx); return <h1>Test, context value: {value}</h1>; } export default function App() { return ( <div> <ctx.Provider value="abc"> <Test /> </ctx.Provider> </div> ); } Copy the code

Callback Hook

Function name: useCallback

A function used to obtain a fixed reference value, usually for performance optimization

useCallback:

This function takes two arguments:

Function, useCallback fixes the reference to the function. As long as the dependency has not changed, it always returns the address array of the previous function

import React, { useState } from 'react';

class Test extends React.PureComponent {
  // Since the props. OnClick is a new function each time it is passed, each parent refresh causes the child to re-render
  render() {
    console.log('Test Render');
    return (
      <div>
        <h1>{this.props.text}</h1>
        <button onClick={this.props.onClick}>Change the text</button>
      </div>); }}function Parent() {
  console.log('Parent Render');
  const [txt, setTxt] = useState(123);
  const [n, setN] = useState(0);
  return (
    <div>{/* The address of the function changes every time it is rendered, causing the subcomponents to be re-rendered. If the subcomponents are optimized, the optimization may fail */}<Test
        text={txt}
        onClick={()= >{ setTxt(Math.random()); }} / ><input
        type="number"
        value={n}
        onChange={(e)= >{ setN(parseInt(e.target.value)); }} / ></div>
  );
}

export default function App() {
  return (
    <div>
      <Parent />
    </div>
  );
}
Copy the code

The following method returns a new function directly, which will also result in a re-rendering

import React, { useState } from 'react';

class Test extends React.PureComponent {
  // Since the props. OnClick passed is a new function each time, each parent refresh causes the child to re-render
  render() {
    console.log('Test Render');
    return (
      <div>
        <h1>{this.props.text}</h1>
        <button onClick={this.props.onClick}>Change the text</button>
      </div>); }}function handleClick(setN) {
  // Returns the new function
  return function (e) {
    setN(parseInt(e.target.value));
  };
}

function Parent() {
  console.log('Parent Render');
  const [txt, setTxt] = useState(123);
  const [n, setN] = useState(0);
  return (
    <div>{/* The address of the function changes every time it is rendered, causing the subcomponents to be re-rendered. If the subcomponents are optimized, the optimization may fail */}<Test
        text={txt}
        onClick={()= >{ setTxt(Math.random()); }} / ><input type="number" value={n} onChange={handleClick(setN)} />
    </div>
  );
}

export default function App() {
  return (
    <div>
      <Parent />
    </div>
  );
}
Copy the code

Solution: Use useCallback

import React, { useState, useCallback } from 'react';

class Test extends React.PureComponent {
  render() {
    console.log('Test Render');
    return (
      <div>
        <h1>{this.props.text}</h1>
        <button onClick={this.props.onClick}>Change the text</button>
      </div>); }}function Parent() {
  console.log('Parent Render');
  const [txt, setTxt] = useState(1);
  const [n, setN] = useState(0);
  const handleClick = useCallback(() = > {
    setTxt(txt + 1);
  }, [txt]);

  return (
    <div>{/* The address of the function changes every time it is rendered, causing the subcomponents to be re-rendered. If the subcomponents are optimized, the optimization may fail */}<Test text={txt} onClick={handleClick} />
      <input
        type="number"
        value={n}
        onChange={(e)= >{ setN(parseInt(e.target.value)); }} / ></div>
  );
}

export default function App() {
  return (
    <div>
      <Parent />
    </div>
  );
}
Copy the code

Memo Hook

Used to keep some data stable, usually for performance optimization

If the React element’s own reference hasn’t changed, it won’t be rerendered

import React, { useState, useMemo } from 'react';
class Test extends React.PureComponent {
  render() {
    console.log('Test Render');
    return (
      <div>
        <h1>{this.props.text}</h1>
        <button onClick={this.props.onClick}>Change the text</button>
      </div>); }}function Parent() {
  console.log('Parent Render');
  const [txt, setTxt] = useState(1);
  const [n, setN] = useState(0);
  //useMemo is different from useCallback
  // useMemo is based on the return value. As long as the dependent data remains unchanged (TXT in this case), its return value always points to the same reference and does not change (i.e. handleClick). The return value can be any type
  // useCallback can only bind to one function. The dependent data does not change, so the function reference does not change
  const handleClick = useMemo(() = > {
    return () = > {
      setTxt(txt + 1);
    };
  }, [txt]);

  return (
    <div>{/* The address of the function changes every time it is rendered, causing the subcomponents to be re-rendered. If the subcomponents are optimized, the optimization may fail */}<Test text={txt} onClick={handleClick} />
      <input
        type="number"
        value={n}
        onChange={(e)= >{ setN(parseInt(e.target.value)); }} / ></div>
  );
}
export default function App() {
  return (
    <div>
      <Parent />
    </div>
  );
}
Copy the code

Ref Hook

UseRef function:

One argument: The default returns a fixed object, {current: value}

Using createRef creates a new ref each time

//arr[0] == arr[1] //false
import React, { useState, useRef } from 'react';
window.arr = [];
export default function App() {
  const inpRef = React.createRef();
  window.arr.push(inpRef); // This is the same as the previous scenario where ref is a function, null is passed, and input is passed
  // So window.arr will look like this after rerendering three times
  // 0: {current: null}
  // 1: {current: null}
  // 2: {current: input}
  const [n, setN] = useState(0);
  return (
    <div>
      <input ref={inpRef} type="text" />
      <button
        onClick={()= >{ console.log(inpRef.current.value); }} > get the value of input</button>
      <input
        type="number"
        value={n}
        onChange={(e)= >{ setN(e.target.value); // Rerender with each click}} ></input>
    </div>
  );
}
Copy the code

With useRef, it’s the same ref every time

// arr[0] == arr[1] //true
import React, { useState, useRef } from 'react';
window.arr = [];
export default function App() {
  const inpRef = useRef();
  window.arr.push(inpRef);
  const [n, setN] = useState(0);
  return (
    <div>
      <input ref={inpRef} type="text" />
      <button
        onClick={()= >{ console.log(inpRef.current.value); }} > get the value of input</button>
      <input
        type="number"
        value={n}
        onChange={(e)= > {
          setN(e.target.value);
        }}
      ></input>
    </div>
  );
}
Copy the code

Use the setInterval timer (almost like setTimeout, no different than writing setTimeout directly)

import React, { useState, useEffect } from 'react';
let timer = null;
export default function App() {
  const [n, setN] = useState(10);
  useEffect(() = > {
    // Run only after mounting
    if (n === 0) {
      clearInterval(timer);
      return;
    }
    timer = setInterval(() = > {
      // setInterval can be replaced with setTimeout
      console.log(n - 1);
      setN(n - 1);
    }, 1000);
    return () = > {
      // Run when the function is unloaded
      clearInterval(timer);
    };
  }, [n]);
  return (
    <div>
      <h1>{n}</h1>
      <button
        onClick={()= > {
          setN(n + 1);
        }}
      >
        n+1
      </button>
    </div>
  );
}
Copy the code

Use the useRef to place the timer in each App component (this will avoid using the same timer for multiple App components)

import React, { useState, useRef, useEffect } from 'react';
export default function App() {
  const [n, setN] = useState(10);
  const timerRef = useRef(); // If you say let timer; Look at the next JS code
  useEffect(() = > {
    if (n === 0) {
      return;
    }
    timerRef.current = setTimeout(() = > {
      console.log(n);
      setN(n - 1);
    }, 1000);
    return () = > {
      clearTimeout(timerRef.current);
    };
  }, [n]);
  return (
    <div>
      <h1>{n}</h1>
    </div>
  );
}
Copy the code
import React, { useState, useRef, useEffect } from 'react';
export default function App() {
  const [n, setN] = useState(10);
  // const timerRef = useRef(); // If you say let timer; It doesn't seem to be a problem
  let timer;
  useEffect(() = > {
    if (n === 0) {
      return;
    }
    timer = setTimeout(() = > {
      console.log(n);
      setN(n - 1);
    }, 1000);
    return () = > {
      clearTimeout(timer);
    };
  }, [n]);
  return (
    <div>
      <h1>{n}</h1>
    </div>
  );
}
Copy the code

Use properties in useRef (useRef is different in each component)

import React, { useState, useRef, useEffect } from 'react';
export default function App() {
  const [n, setN] = useState(10);
  const nRef = useRef(n); // {current:10}
  // nRef = n and flag = n
  useEffect(() = > {
    const timer = setInterval(() = > {
      nRef.current--;
      setN(nRef.current);
      if (nRef.current === 0) {
        clearInterval(timer); }},1000);
    return () = > {
      clearInterval(timer); }; } []);return (
    <div>
      <h1>{n}</h1>
    </div>
  );
}
Copy the code
import React, { useState, useRef, useEffect } from 'react';
export default function App() {
  const [n, setN] = useState(10);
  // const nRef = useRef(n); // {current:10}
  var flag = n;
  useEffect(() = > {
    const timer = setInterval(() = > {
      flag--;
      setN(flag);
      if (flag === 0) {
        clearInterval(timer); }},1000);
    return () = > {
      clearInterval(timer); }; } []);return (
    <div>
      <h1>{n}</h1>
    </div>
  );
}
Copy the code

ImperativeHandle Hook

Function: useImperativeHandleHook

import React, { useRef } from 'react';
class Test extends React.Component {
  method() {
    console.log('Test method called');
  }

  render() {
    return <h1>Test Component</h1>; }}export default function App() {
  const testRef = useRef();
  return (
    <div>
      <Test ref={testRef} />
      <button
        onClick={()= >{ testRef.current.method(); }} > Click to call the method method of the Test component</button>
    </div>
  );
}
Copy the code
import React, { useRef, useImperativeHandle } from 'react';

function Test(props, ref) {
  useImperativeHandle(
    ref,
    () = > {
      // If no dependency is given, this method is called every time the function component is run
      // If a dependency is used, the function will be cached after the first call and will only be called again if the dependency changes
      // return 1;
      // return 1; Equivalent to giving ref. Current = 1
      return {
        method() {
          console.log('Test Component Called'); }}; } []);return <h1>Test Component</h1>;
}

const TestWrapper = React.forwardRef(Test);

// class Test extends React.Component {

// method() {
// console.log("Test method called");
/ /}

// render() {
// return 

Test Component

/ /} // } export default function App() { // const [, forceUpdate] = useState({}) const testRef = useRef(); return ( <div> <TestWrapper ref={testRef} /> <button onClick={()= >{ testRef.current.method(); // console.log(testRef) // forceUpdate({})}} > Click to call the method method of the Test component</button> </div> ); } Copy the code

Examples from the official website

function FancyInput(props, ref) {
  // ref gets the parent component's testRef
  const inputRef = useRef();
  useImperativeHandle(ref, () = > ({
    UseImperativeHandle Returns the value of the parent component's testref.current
    focus: () = >{ inputRef.current.focus(); }}));return <input ref={inputRef} />;
}

const TestWrapper = React.forwardRef(FancyInput);

export default function App() {
  // const [, forceUpdate] = useState({})
  const testRef = useRef();
  return (
    <div>
      <TestWrapper ref={testRef} />
      <button
        onClick={()= >{ // testRef.current.method(); console.log(testRef); ƒ} // forceUpdate({})}} > Click on the method method of the Test component</button>
    </div>
  );
}
Copy the code

LayoutEffect Hook

UseEffect: after the browser rendering is complete and the user sees the new rendering result useLayoutEffectHook: The DOM changes are complete but not yet presented to the user

UseEffect should be used as much as possible, as it does not block rendering, and if there is a problem, then use useLayoutEffectHook for reasons: If you call a dom operation for 5 seconds in the useLayoutEffectHook, it will cause browser rendering delays, and the user will not see the effect, and the experience will not be good

  1. Synchronization of DOM changes (setN function below)
  2. ComponentDidMount&componentDidUpdate (componentDidMount&componentDidUpdate
  3. The browser next renders the point in time to achieve the contrast difference, rendering the user to see the new effect
  4. Call useEffect(so dom changes here will have a flicker effect (rerender))
import React, { useState, useLayoutEffect, useRef } from 'react';
export default function App() {
  const [n, setN] = useState(0);
  const h1Ref = useRef();
  useLayoutEffect(() = > {
    / / flicker-free
    // The call time is equivalent to the class component componentDidMount&componentDidUpdate
    h1Ref.current.innerText = Math.random().toFixed(2);
  });
  UseEffect (() => {// There is a flicker
  // h1Ref.current.innerText = Math.random().toFixed(2);
  // })
  return (
    <div>
      <h1 ref={h1Ref}>{n}</h1>
      <button
        onClick={()= >{ setN(n + 1); > +}}</button>
    </div>
  );
}
Copy the code

DebugValue Hook

UseDebugValue: Used to display the associated data of custom hooks to the debug bar

If the created custom Hook has high generality, useDebugValue can be used to facilitate debugging

import React, { useState, useEffect, useDebugValue } from 'react';

function useTest() {
  const [students] = useState([]);
  useDebugValue('Student Assembly');
  return students;
}

export default function App() {
  useState(0);
  useState('abc');
  useEffect(() = > {
    console.log('effect'); } []); useTest();return <div></div>;
}
Copy the code