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