1. Introduction to Hook

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

Stick code directly,hook to achieve simple version of the counter

Simple debugger code

  • We can see that hooks use memorizedState to hold state

2. Simple version implementation of useState

  • The core function is to add a state holding function to the function component
letmemoizedState; / / declare memoizedStatefunction useState(initialState) {
    memoizedState = memoizedState || initialState;
    function setState(newState) { memoizedState = newState; MemoizedState Render (); // memoizedState render(); // rerender}return [memoizedState,setState]
}
Copy the code

From the simple version of the implementation, or very easy to understand, test the effect

3. Introduction and implementation of userReducer

3.1. Introduction to userReducer

  • An alternative to useState. It receives a Reducer of the form (state, action) => newState and returns the current state and its accompanying dispatch method. (If you’re familiar with Redux, you already know how it works.)
  • UseReducer can be more useful than useState in some situations, such as when the state logic is complex and contains multiple subvalues, or when the next state depends on the previous state.

UserReducer implementing counter, can receive three parameters, reducer | initalArg (initial value) | init (define the initial value of the function)

import React,{Fragment,useReducer} from 'react';
import ReactDOM from 'react-dom'; // Reducer, same as redux const INCREMENT ="INCREMENT";
const DECREMENT = "DECREMENT";
function reducer(state,action){
    switch (action.type) {
        case INCREMENT:
            return {number:state.number+1};
        case DECREMENT:
            return {number:state.number-1};
        default:
            returnstate; }} // Initial valueletinitalArg = 0; // A function that returns the initial valuefunction init(initalArg) {
    return {number:initalArg};
}

function Counter() {
    // state = {number:0}
    const [state,dispatch] = useReducer(reducer,initalArg,init);
    return (
        <Fragment>
            <p>{state.number}</p>
            <button onClick={()=>dispatch({type:INCREMENT})}>+</button>
            <button onClick={()=>dispatch({type:DECREMENT})}>-</button>
        </Fragment>
    )
}
function render() {
    ReactDOM.render(<Counter />,document.getElementById('root'));
}
render();

Copy the code

3.2. Implementation principle of simplified version

letmemoizedState; // Declare the state of memoryfunction useReducer(reducer,initalArg,init) {
    letinitialState; // init If no value is passed, initalArg is the default initial state. If a value is passed, the initial value function takes it as the initial stateif(typeof init ! ='undefined'){
        initialState = init(initalArg);
    }else {
        initialState = initalArg;
    }
    memoizedState = memoizedState || initialState;
    function dispatch(action) {
        memoizedState = reducer(memoizedState,action);
        render();
    }

    return [memoizedState,dispatch]
}

Copy the code

The results

3.3, useReducer is the internal implementation of useState, rewrite useState implementation

let memoizedState;
function useReducer(reducer,initalArg,init) {
    let initialState;
    if(typeof init ! ='undefined'){
        initialState = init(initalArg);
    }else {
        initialState = initalArg;
    }
    memoizedState = memoizedState || initialState;
    function dispatch(action) {
        memoizedState = reducer(memoizedState,action);
        render();
    }

    return [memoizedState,dispatch]
}

functionUseState (initialState) {// This is a reducer implementationreturn useReducer((oldState,newState)=>newState,initialState);
}

Copy the code

Verify the following

4. Multiple useState calls simultaneously

When a component calls multiple Usestates, we need arrays to hold multiple initial values

4.1. Several examples used by useState

  • Two buttons, one to change name and one to change number

4.2 Implementation principle

  • Previously, one useState was used. When there are multiple usEstates, you need to store all the initial states in an array
  • You need to use index to record the current index
  • Every time I render, the index needs to return to its initial value
import React,{Fragment} from 'react';
import ReactDOM from 'react-dom'; // Array saves memoizedStateletmemoizedState=[]; // Record indexlet index = 0;
functionuseState(initialState) { memoizedState[index] = memoizedState[index] || initialState; // Cache the current index, because each time render, index is reset to 0let currentIndex = index;
    function setState(newState) {
        memoizedState[currentIndex] = newState;
        render();
    }
    return [memoizedState[index++],setState]
}

function Counter() {
    const [name,setName] = useState('counter');
    const [number,setNumber] = useState(0);
    return (
        <Fragment>
            <p>{name }:{number}</p>
            <button onClick={()=>setName("Counter"</button> <button onClick={()=>setNumber(number+1)}>+</button>
        </Fragment>
    )
}
function renderRender () {render (); render () {render (); render (); render (); render (); ReactDOM.render(<Counter />,document.getElementById('root'));
}
render();

Copy the code

Look at the effect, the source is achieved with a linked list, here we use an array, the logic is similar, easy to understand

5. Introduction and implementation of useEffect

5.1, introduction to

  • UseEffect adds the ability to function components to manipulate side effects, such as subscribing and unsubscribing events, setting and emptying timers
  • Similar to what componentDidMount and componentWillUnmount do in the class component lifecycle

Example reference link for printing a sentence of log after the counter changes


import React,{Fragment,useState} from 'react';
import ReactDOM from 'react-dom';

function Counter() {
    const [name,setName] = useState('counter');
    const [number,setNumber] = useState(0); UseEffect (()=>{// subscribe console.log()"Subscription Status")
    },[number,name]);
    return (
        <Fragment>
            <p>{name }:{number}</p>
            <button onClick={()=>setName("Counter"</button> <button onClick={()=>setNumber(number+1)}>+</button>
        </Fragment>
    )
}
function render() {
    ReactDOM.render(<Counter />,document.getElementById('root'));
}
render();

Copy the code

5.2. Simple version implementation

  • UseEffect The second argument is the dependency, that is, the callback is triggered when the dependency changes

Jane version of the implementation

// Record the last dependencylet lastDependencies;
functionUseEffect (callback,dependencies) {// If the dependency does not pass a value, call callbackif(! dependencies)returncallback(); /* first render isChange totrueWhen rendering again, compare lastDependencies to dependencies and trigger the callback */ only when they are not exactly equalletisChange = lastDependencies? ! dependencies.every((item,index)=>item===lastDependencies[index]):true;
    if(isChange){ callback(); lastDependencies = dependencies; }}Copy the code

5.3 How to implement useeffects when there are multiple useeffects

  • Put lastDependencies into memoizedState in useState
let memoizedState=[];
let index = 0;
function useState(initialState) {
    memoizedState[index] = memoizedState[index] || initialState;
    let currentIndex = index;
    function setState(newState) {
        memoizedState[currentIndex] = newState;
        render();
    }
    return [memoizedState[index++],setState]
}

function useEffect(callback,dependencies) {
    console.log('dependencies',dependencies); // If the dependency does not pass a value, callback is called directlyif(! Dependencies){// ensure that the index corresponds to index++;returncallback(); } // Take the last dependency from memoizedStatelet lastDependencies = memoizedState[index];
    letisChange = lastDependencies? ! dependencies.every((item,index)=>item===lastDependencies[index]):true;
    if(isChange){ callback(); // memoizedState[index] = dependencies; } // index increment index++}Copy the code

Let’s verify the effect

Continue to supplement