Hook is a new feature in React 16.8. It lets you use state and other React features without having to write a class

React Hook React Hook In short, it seems to me that functional components (stateless components) can have their own state and methods for changing state (setState).

React Hook why does it exist

  1. Reusing state logic between components is difficult

We could have done this with render props and higher-order components, but! But! Not only is it easy to create “nesting hell,” but it also presents an awkward problem (compared to me, a novice) that complex logic is hard to write! Simple logic I’d rather rewrite than use higher-order components is much more convenient (you can copy and paste, after all).

  1. Complex components are difficult to understand

Various life cycle functions are filled with various state logic processing and side effects, and it is difficult to reuse, scattered, such as a call list data interface method getList respectively to write componentDidMount and componentDidUpdate and so on.

  1. Hard to understand class

The orientation of this (forgetting bind(this) at one point and looking for a bug), component precompilation techniques (component folding) that encounter optimization failures in class cases, classes that don’t compress well, and classes that are unstable when hot reloaded.

So! React Hook Doesn’t have to write classes, components can write functions, lifecycle hooks, and most importantly, this.

State Hook

First let’s look at a simple example given by the authorities

This is a simple cumulative counter. Let’s look at the class declaration component first

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return<div> <p> <button onClick={() => this.setState({count: This.state.count + 1})}> </button> </div>); }}Copy the code

React is a very simple component that anyone who knows react will understand

So let’s look at the version that uses hook

import React, { useState } from 'react';

function Example() {// declare a call"count"State variable const [count,setCount] = useState(0);

  return(< div > < p > once you click the {count} < / p > < button onClick = {() = >set</button> </div>); }Copy the code

Is the hook a little simpler? As you can see, this is a function that has its own state (count), just like this.state.count in class; It can also update its state with setCount(), like this.setState() in class.

Why is that? Look closely at the first line where we introduce the useState hook, which makes our stateless component a stateful one.

Declaration, reading, and updating of status values

So let’s break it down and see what this hook has done for us

const [count, setCount] = useState(0);
Copy the code

First, useState is used to declare state variables. This function takes the initial value for the variable and returns an array with [0] as the current state value and [1] as the method to update the state value

So this is essentially declaring a count variable, just like

this.state = {
      count: 0
    };
Copy the code

It also assigns count an initial value of 0 by passing the argument 0 to useState(0), and provides a method called setCount that can update it like this.setstate

We can then refer to the variable as {count} instead of this.state.count.

<p> You clicked {count} times </p>Copy the code

When we want to update this value, we simply call setCount(the new value).

How about that? Is it simple and easy to understand?

But, but! Aye? No, how does this function remember the previous state? The Example function is used to declare a variable in a function, and then the function is destroyed when it is finished.

State Hook solution to storage persistence

The methods I know of for storing persistent state in JS are: class classes, global variables, DOM, closures

React Hook source code parsing to learn that it is implemented using closures, in-depth I do not understand! This is how useState is implemented in the first place

function useState(initialState){
    let state = initialState;
    function dispatch = (newState, action)=>{
        state = newState;
    }
    return [state, dispatch]
}


Copy the code

Does it look like Redux? Given an initial state, an action is dispatched, the state is changed by reducer, and the new state is returned to trigger the re-rendering of the component.

But this alone is not enough. We need a new data structure to hold the last state and the current state so that we can get the correct values when the initialization process calls useState and when the update process calls useState. Suppose this data structure is called Hook:

typeHook = {memoizedState: any, / / the last complete update after the final state of the value of the queue: UpdateQueue < any, any > | null, / / update the queue};Copy the code

Considering the differences between the mounting of the first component and the subsequent updating logic, we defined two useState functions called mountState and updateState respectively

function useState(initialState){
    if(isMounting){
        return mountState(initialState);
    }
    
    if(isUpdateing){
        returnupdateState(initialState); }} // The method actually called the first time the component's useState is calledfunction mountState(initialState){
    let hook = createNewHook();
    hook.memoizedState = initalState;
    return [hook.memoizedState, dispatchAction]
}

functionDispatchAction (action){// Use data structures to store all update actions so that the latest state value storeUpdateActions(action) is calculated in rerender; // Execute fiber's scheduleWork(); } // The method actually called each time useState is executed after the first timefunctionUpdateState (initialState){// Calculate the new status value based on the update action stored in dispatchAction and return it to the componentdoReducerWork();
    
    return [hook.memoizedState, dispatchAction];
}   

function createNewHook() {return {
        memoizedState: null,
        baseUpdate: null
    }
}
Copy the code

React Hook Build process

Want to understand the source code principle please jump ~

Declare multiple state variables

UseState can be called multiple times

function ExampleWithManyStates() {// declare multiple state variables const [age,setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'learning Hook' }]);
  // ...
}
Copy the code

UseState also supports taking objects or arrays as arguments. Unlike this.setState, which returns a new state after merging the state, useState simply replaces the old state and returns the new state.

Rules of the Hook

A hook is essentially a javaScript function, but it has two rules to follow when using it. React requires that these two rules be enforced, otherwise strange bugs will appear.

1. Use hooks only at the top level

Never call a hook in a loop, condition, or nested function. Always call a hook at the top of the React function. This is to ensure that the hooks are called in the same order every time you render react. The correct state is maintained between calls to ensure independence and one-to-one correspondence between states.

2. Only call a hook in the React function

Do not call a hook in a normal JS function

Here’s an example of # 1:

function Form() {
  const [name1, setName1] = useState('zhangsan');
  const [name2, setName2] = useState('lisi');
  const [name3, setName3] = useState('wangwu');
  // ...
}
Copy the code

UseState is used three times in a row to declare name1, name2, and name3 with initial values, so it looks like this at render time

// First render (assign initial value) useState('zhangsan') // 1'zhangsan'Initialize state useState(name1)'lisi') // 2'lisi'Initialize state useState(name2)'wangwu') // 3'wangwu'Initialize state variable named name3 // render useState('zhangsan'UseState (); // 1.'lisi'// 2. Fetch name2 useState('wangwu') // 3. Read name3Copy the code

If we write it this way!

let tag = true;
function Form() {
  const [name1, setName1] = useState('zhangsan');
  if (tag) {
    const [name2, setName2] = useState('lisi');
    tag = false;
}
  const [name3, setName3] = useState('wangwu');
}
Copy the code

The process is going to be zero

// First render (assign initial value, same as above) useState('zhangsan') // 1'zhangsan'Initialize state useState(name1)'lisi') // 2'lisi'Initialize state useState(name2)'wangwu') // 3'wangwu'Initialize state variable named name3 // render useState('zhangsan'// 1. Fetch name1 //useState('lisi'UseState (); // 2.'wangwu') // 3. The value of name3 is read by the sequence judgment, but name2 is readCopy the code

So hook rules are actually to ensure the order of hook execution, because it is through the order to complete the one-to-one correspondence and mutual independence!

Effect Hook

Note that Effect Hook allows you to perform side effects on function components. If this doesn’t give you a good idea of what it does, we can take a look at the simplest counter above and add a small function to it

function Example() {
  const [count, setCount] = useState(0); UseEffect (() => {// Set the browser title content document.title = 'you clicked${count}`; });return(< div > < p > once you click the {count} < / p > < button onClick = {() = >set</button> </div>); }Copy the code

This code adds a message that sets the title of the document to include the number of clicks. How do I do that with the class component? Let’s compare:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {document.title = 'You clicked${this.state.count}`; }componentDidUpdate() {document.title = 'You clicked${this.state.count}`; }render() {
    return<div> <p> <button onClick={() => this.setState({count: This.state.count + 1})}> </button> </div>); }}Copy the code

By contrast, it can be seen that effect Hook is equivalent to the life cycle componentDidMount, componentDidUpdate; In fact, stateful components often have side effects, such as Ajax requests, binding and unbinding of browser events, manual DOM modification, logging, etc. Effect Hook is also equivalent to a componentWillUnmount (for example, if we have a requirement to request the latest data from the server, we need to clean up the polling operation when the component is uninstalled).

Clearance side effect

componentDidMount(){this.getNewData()}componentWillUnmount(){this.ungetNewData ()}Copy the code

An effect hook can be used to remove this effect from a functional component by returning a function within an effect function.

useEffect(()=>{
    getNewData()
    return function cleanup() {
        unGetNewData()
    }
})
Copy the code

Effect returns a function, which is an optional cleanup mechanism for effect. Each effect can return a cleanup function, depending on your needs.

React performs cleanup operations when components are uninstalled. Effect is run on every render and is cleaned up before each render to remove the last effect side effect.

Need to pay attention is!! This unbinding mode is different from componentWillUnmount. ComponentWillUnmount is executed only once before the component is destroyed, whereas Effect Hook functions are executed every time the component is rendered, including side effects and cleanup operations for their return

Effect performance optimization

“Every time” is executed, the first thing you will think of is related to performance, so in fact, effect hook can be skipped, also through the comparison of the class component to understand the effect hook, the class component uses componentDidUpdate to compare the logic before and after

componentDidUpdate(prevProps, prevState) {
  if(prevState.count ! Document. title = 'you clicked this${this.state.count}`; }}Copy the code

Similarly, in effect, there is a second parameter that does the same thing

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

The second argument is an array. If there are multiple elements in the array, React will execute effect even if only one element changes. An empty array [] indicates that the effect will only be executed once (including side effects and return side effect cleanup operations)

Customize the Hook

That was our original goal: logic reuse! Imagine that we now want to implement such a feature component, click button to randomly switch the background color, as shown in the picture

import React, { Component } from "react";

export default class ChangeColor extends Component {
  constructor() {
    super();
    this.state = {
      color: "red"
    };
    this.colors = ["red"."bule"."green"."yellow"."black"];
  }

  changeColor() {
    const index = Math.floor(Math.random() * this.colors.length);
    this.setState({ color: this.colors[index] });
  }

  render() {
    return (
      <div>
        <div
          style={{
            width: 400,
            height: 100,
            border: "1px solid #ccc"</div> <button onClick={() => this.changecolor ()}> </button> </div>); }}Copy the code

So how does hook work

import React, { useState } from "react";

export default function ChangeColor() {
  const colors = ["red"."bule"."green"."yellow"."black"];
  const [color, setColor] = useState("red");
  function changeColor() {
    const index = Math.floor(Math.random() * colors.length);
    setColor(colors[index]);
  }
  return (
    <div>
      <div
        style={{
          width: 400,
          height: 100,
          border: "1px solid #ccc"</button onClick={changeColor}> </button> </div>); }Copy the code

So it doesn’t look like there’s a big difference between the two methods, just the way they’re written. So the problem comes, now we have a demand at the same time, and it is very similar to it, but instead of clicking switch, but the timer automatically switch (slide switch, anyway a variety of switching methods ~). This class component is stupid and cannot be reused. It has the click event logic and DOM, and all we can reuse is the color switching logic! If you can’t copy and paste and write a new one, it’s short anyway. What if it’s complicated logic? How to do? At this time we look at a custom hook, it can help us solve this problem, we need to separate the logic of reuse, to achieve elegant reuse.

First of all, the only logic that can be reused is the logic to switch colors, so what we extract must be pure logic, that is, the CUSTOM hook should not contain the DOM

import { useState } from "react";

export default function useRandomColor() {
  const colors = ["red"."bule"."green"."yellow"."black"];
  const [color, setColor] = useState("red");
  function changeColor() {
    const index = Math.floor(Math.random() * colors.length);
    setColor(colors[index]);
  }

  return [color, changeColor];
}
Copy the code

Here we have a custom hook called useRandomColor that returns [color, method to change color], isn’t that like useState? This is a simple custom hook by us. It looks like this when we call it in the parent component

import React from "react";
import useRandomColor from "./changeColor";

export default function ChangeColor() {
  const [color, setColor] = useRandomColor();

  return (
    <div>
      <div
        style={{
          width: 400,
          height: 100,
          border: "1px solid #ccc",
          background: color
        }}
      ></div>
      <button onClick={set</button> </div>); }Copy the code

It’s exactly the same as useState, isn’t it?

It is important to note that a custom hook is a function whose name begins with “use” and whose interior can call other hooks (either provided by the API or customized).

Must custom hooks start with “use”? It has to be. This agreement is very important. Otherwise, React will not automatically check if your hooks violate Hook rules, since it is impossible to determine whether a function contains calls to its internal hooks.

Will using the same Hook in two components share state? Don’t. A custom Hook is a mechanism for reusing state logic (for example, set to subscribe and store current values), so every time a custom Hook is used, all state and side effects are completely isolated.

What other hooks react provides

Here I only talk about useState and useEffect, the two most important and most commonly used hooks (for me at the current stage). Actually react also provides many hooks:

  • useContext
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeMethods
  • useMutationEffect
  • useLayoutEffect

Insight into the usage can jump: react-1251415695.cos-website.ap-chengdu.myqcloud.com/docs/hooks-… In view of my limited and insufficient abilities, I will study and study the application methods and scenarios of these hooks in the future.

reference

  • The official documentation
  • React Hook build process
  • Why do we embrace React Hook