天天看點

這一次,學會手寫useEffect

關于原生的useEffect的基本用法,可以看我的這篇文章 這一次,徹底搞懂useEffect ,簡潔易懂。

原生useEffect具備的幾個特點

  1. useEffect可以多次調用。
  2. useEffect根據傳入參數的不同,具有不同的執行方法。

手寫useEffect的步驟

第一步:使用數組來存儲不同的effect

// 以前的依賴值
let preArray = [];
// 定義effect的索引
let effectId = 0;
複制代碼      

第二步:判斷傳入參數是否正确

  • 如果第一個參數傳入的不是函數則報錯
// 如果第一個參數不是一個函數則報錯
if (Object.prototype.toString.call(callback) !== '[object Function]') {
    throw new Error('第一個參數不是函數')
}
複制代碼      
  • 判斷第二個參數
    • 沒傳的話,按照componentDidMount 和 componentDidUptate處理
    • 傳的話,判讀是不是數組,不是則報錯
    • 擷取前一個effect,如果沒有則等同于compoentDidMout,直接執行callback,如果有則判斷是否與以前的依賴值一樣,如果不一樣則執行callback,這樣就實作了第二個參數數組内的元素發生變化的時候才執行callback.
// 如果第二個參數不傳,相當于componentDidMount和componentDidUpdate
if (typeof array === 'undefined') {
    callback();
} else {
    // 判斷array是不是數組
    if (Object.prototype.toString.call(array) !== '[object Array]') throw new Error('useEffect的第二個參數必須是數組')
    // 擷取前一個的effect
    if (preArray[effectId]) {
        // 判斷和以前的依賴值是否一緻,一緻則執行callback
        let hasChange = array.every((item,index) => item === preArray[effectId][index]) ? false : true;
        if (hasChange) {
            callback();
        }
    } else {
        callback()
    }
複制代碼      

第三步:更新依賴值

注意,本次實作的useEffect是需要render函數執行的時候,将effectId置為0的。
// 更新依賴值
preArray[effectId] = array;
effectId++;
複制代碼      

全部代碼

import React from 'react'
import ReactDOM from 'react-dom'
// 自定義Hook
// 自定義useState
let states = [];
let setters = [];
let stateid = 0;
function render() {
    stateid = 0;
    effectId = 0;
    ReactDOM.render(<App />,document.querySelector('#root'));
}
function createSetter(stateid) {
    return function (newState) {
        states[stateid] = newState;
        render()
    }
}
function myUseState(initialState) {
    states[stateid] = states[stateid] ? states[stateid] : initialState;
    setters.push(createSetter(stateid));
    let value = states[stateid];
    let setter = setters[stateid];
    stateid++;
    return [value,setter]
}
// 以前的依賴值
let preArray = [];
// 定義effect的索引
let effectId = 0;
function myUseEffect(callback,array) {
    // 如果第一個參數不是一個函數則報錯
    if (Object.prototype.toString.call(callback) !== '[object Function]') {
        throw new Error('第一個參數不是函數')
    }
    // 如果第二個參數不傳,相當于componentDidMount和componentDidUpdate
    if (typeof array === 'undefined') {
        callback();
    } else {
        // 判斷array是不是數組
        if (Object.prototype.toString.call(array) !== '[object Array]') throw new Error('useEffect的第二個參數必須是數組')
        // 擷取前一個的effect
        if (preArray[effectId]) {
            // 判斷和以前的依賴值是否一緻,一緻則執行callback
            let hasChange = array.every((item,index) => item === preArray[effectId][index]) ? false : true;
            if (hasChange) {
                callback();
            }
        } else {
            callback()
        }
        // 更新依賴值
        preArray[effectId] = array;
        effectId++;
    }
}
function App() {
    const [count,setCount] = myUseState(0);
    const [name,setName] = myUseState('張三');
    myUseEffect(() => {
        console.log('這是count');
    },[count]);
    myUseEffect(() => {
        console.log('這是name');
    },[name]);
    return (
        <div>
            <h1>目前求和為:{count}</h1>
            <button onClick={() => setCount(count + 1)}>點我+1</button>
            <h1>目前姓名為:{name}</h1>
            <button onClick={() => setName('李四')}>點我修改姓名</button>
        </div>
    )
}
ReactDOM.render(<App />, document.querySelector('#root'));
複制代碼      

線上實作