Hooks的原理/问题/以及注意事项

Hooks的原理

- 单向链表通过next把hooks串联起来
- memoizedState存在fiber node上,组件之间不会相互影响
- useState和useReducer中通过dispatchAction调度更新任务

Hooks的使用注意事项

  • 只能在顶层调用Hooks? Hooks是使用数组或单链表串联起来,Hooks顺序修改会打乱执行结果
  • useState在多个组件中引入,彼此之间会不会有影响? 在React中Hooks把数据存在fiber node上的,每个组件都有自己的currentlyRenderingFiber.memoizedState

Hooks的问题

  • 只能在顶层调用Hooks? Hooks是使用数组或单链表串联起来,Hooks顺序修改会打乱执行结果
  • useState在多个组件中引入,彼此之间会不会有影响? 在React中Hooks把数据存在fiber node上的,每个组件都有自己的currentlyRenderingFiber.memoizedState

手动封装简单的HOOK函数

myHook / hooks.js

const hooks = (function() {
    const HOOKS = [];
    let currentIndex = 0;
    const Tick = {
        render: null,
        queue: [],
        push: function(task) {
            this.queue.push(task);
        },
        nextTick: function(update) {
            this.queue.push(update);
            Promise.resolve(() => {
                if (this.queue.length) { // 一次循环后,全部出栈,确保单次事件循环不会重复渲染
                    this.queue.forEach(f => f()); // 依次执行队列中所有任务
                    currentIndex = 0; // 重置计数
                    this.queue = []; // 清空队列
                    this.render && this.render(); // 更新dom
                }
            }).then(f => f());
        }
    };
  
    function useState(initialState) {
        HOOKS[currentIndex] = HOOKS[currentIndex] || (typeof initialState === 'function' ? initialState() : initialState);
        const memoryCurrentIndex = currentIndex; // currentIndex 是全局可变的,需要保存本次的
        const setState = p => {
              let newState = p;
              if (typeof p === 'function') {
                  newState = p(HOOKS[memoryCurrentIndex]);
              }
              if (newState === HOOKS[memoryCurrentIndex]) return;
              Tick.nextTick(() => {
                  HOOKS[memoryCurrentIndex] = newState;
              });
        };
        return [HOOKS[currentIndex++], setState];
    }
    function useEffect(fn, deps) {
        const hook = HOOKS[currentIndex];
        const _deps = hook && hook._deps;
        const hasChange = _deps ? !deps.every((v, i) => _deps[i] === v) : true;
        const memoryCurrentIndex = currentIndex; // currentIndex 是全局可变的
        if (hasChange) {
            const _effect = hook && hook._effect;
            setTimeout(() => {
                typeof _effect === 'function' && _effect(); // 每次先判断一下有没有上一次的副作用需要卸载
                const ef = fn();
                HOOKS[memoryCurrentIndex] = {...HOOKS[memoryCurrentIndex], _effect: ef}; // 更新effects
            })
        }
        HOOKS[currentIndex++] = {_deps: deps, _effect: null};
    }
    function useReducer(reducer, initialState, initialAction) {
        initialAction && (initialState = initialAction(initialState))
        const [state, setState] = useState(initialState);
        const update = (state, action) => {
            const result = reducer(state, action);
            setState(result);
        }
        const dispatch = update.bind(null, state);

        return [state, dispatch];
    }
    function useMemo(fn, deps) {
        const hook = HOOKS[currentIndex];
        const _deps = hook && hook._deps;
        const hasChange = _deps ? !deps.every((v, i) => _deps[i] === v) : true;
        const memo = hasChange ? fn() : hook.memo;
        HOOKS[currentIndex++] = {_deps: deps, memo};
        return memo;
    }
    function useCallback(fn, deps) {
        return useMemo(() => fn, deps);
    }
    return {
      Tick, useState, useEffect, useReducer, useMemo, useCallback
    }
  })();

  export default hooks;

myHooks / index.js

const memoize = fn => {
    const cache = Object.create(null);
    return value => cache[value] || (cache[value] = fn(value));
};

在react目录下 index.js 中:

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

import hooks from './utils/mockHooks';

// 挂在根组件
import App from './views/useState';
// import App from './views/useEffect';
// import App from './views/useRef';
// import App from './views/useMemo';
// import App from './views/useContext';
// import App from './views/useReducer';


function RenderUI(){
    ReactDOM.render(<App />, document.getElementById('root'));
}

RenderUI();

// 在Tick上挂在render方法
hooks.Tick.render = RenderUI;

// 触发组件更新的几种方式:setState, forceUpdate, render, dispatchAction

你可能感兴趣的:(React,HOOKs)