React Hook Provides the useEffect hook for function components, which can simulate lifecycle functions and perform side effects inside functions.

First, a summary of its usage:

  1. UseEffect supports two parameters: the first parameter callback is a function type, and the second parameter, if passed, must be an array type.
  2. If the second argument is not passed, the callback is executed each time the state-change function is re-executed
  3. If the second argument is passed an empty array, it is executed only once at initialization
  4. If the second argument is passed in a non-empty array, the callback is executed every time the state in the dependency changes

The implementation code is as follows

// Use to store the dependency array passed in each time useEffect is called
let prevDepsAry = []
// Use an index to record the dependency array for each callback function
let effectIndex = 0

function useEffect(callback, depsAry) {
  // Check whether the parameter type is correct
  // If callback is not a function type, an error is reported
  if(Object.prototype.toString.call(callback) ! = ='[object Function]') throw new Error(`${callback}Must be a function type ')
  // Check whether the dependent array is passed in
  if(depsAry === undefined) {
    // The callback function is executed every time the function is called again
    callback()
  } else {
    // check if depsAry is an array type. If depsAry is not an array type, report an error
    if(Object.prototype.toString.call(depsAry) ! = ='[object Array]') throw new Error(`${depsAry}Must be an array type ')
    // Is an array type, then we need to get the last dependent array, item by item to compare whether the change
    let prevDeps = prevDepsAry[effectIndex]
    // Check whether the prevDeps exists
    const hasChanged = prevDeps ? depsAry.every((dep, index) = > dep === prevDeps[index]) === false : true
    if(hasChanged) {
      // If a dependency changes, call callback
      callback()
    }
    // Synchronize the dependency array after this change
    prevDepsAry[effectIndex] = depsAry
    effectIndex++
  }
}

// Since state changes refresh the view, the reactdom.render method is used to simulate refreshing the view after state changes
function render() {
  // Reset stateIndex every time render is called, otherwise the corresponding indeterminate increment index will not properly match the relationship between state and setState
  // stateIndex = 0
  // Every time render is called, effectIndex must be reset, otherwise the corresponding index will be infinitely incremented and callback will not match the corresponding dependent array
  effectIndex = 0
  ReactDom.render(<App />.document.getElementById('root'))}Copy the code

The basic functions of the above useEffect have been implemented.

Here is the complete test code (which also includes a self-wrapped useState function that can be replaced with the react function) :

import React from 'react'
import ReactDom from 'react-dom'

// An array to store state
let state = []
// An array of methods that store the change state
let setters = []
// The subscript used to record the relationship between the state and the change state method
let stateIndex = 0

function createSetter(index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState(initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  // Use closures to cache the setState corresponding to each state
  setters.push(createSetter(stateIndex))
  const value = state[stateIndex]
  const setter = setters[stateIndex]
  // After each group is created, +1 is used as the index for the next group status
  stateIndex++
  return [value, setter]
}

// Since state changes refresh the view, the reactdom.render method is used to simulate refreshing the view after state changes
function render() {
  // Reset stateIndex every time render is called, otherwise the corresponding indeterminate increment index will not properly match the relationship between state and setState
  stateIndex = 0
  // Every time render is called, effectIndex must be reset, otherwise the corresponding index will be infinitely incremented and callback will not match the corresponding dependent array
  effectIndex = 0
  ReactDom.render(<App />.document.getElementById('root'))}// Use to store the dependency array passed in each time useEffect is called
let prevDepsAry = []
// Use an index to record the dependency array for each callback function
let effectIndex = 0

function useEffect(callback, depsAry) {
  // Check whether the parameter type is correct
  // If callback is not a function type, an error is reported
  if(Object.prototype.toString.call(callback) ! = ='[object Function]') throw new Error(`${callback}Must be a function type ')
  // Check whether the dependent array is passed in
  if(depsAry === undefined) {
    // The callback function is executed every time the function is called again
    callback()
  } else {
    // check if depsAry is an array type. If depsAry is not an array type, report an error
    if(Object.prototype.toString.call(depsAry) ! = ='[object Array]') throw new Error(`${depsAry}Must be an array type ')
    // Is an array type, then we need to get the last dependent array, item by item to compare whether the change
    let prevDeps = prevDepsAry[effectIndex]
    // Check whether the prevDeps exists
    const hasChanged = prevDeps ? depsAry.every((dep, index) = > dep === prevDeps[index]) === false : true
    if(hasChanged) {
      // If a dependency changes, call callback
      callback()
    }
    // Synchronize the dependency array after this change
    prevDepsAry[effectIndex] = depsAry
    effectIndex++
  }
}

// Implement useState useEffect manually
function App() {

  const [count, setCount] = useState(0)
  const [title, setTitle] = useState('useState')

  useEffect(() = > {
    console.log('useEffect');
  }, [])

  useEffect(() = > {
    console.log('useEffect count');
  }, [count])

  useEffect(() = > {
    console.log('useEffect every time');
  })

  return (
    <div>
      <h1>{title}</h1>
      <button onClick={()= >SetTitle ('useState Success')}> Modify title</button>
      <h1>{count}</h1>
      <button onClick={()= > setCount(count + 1)}>count++</button>
    </div>)}export default App
Copy the code