背景:react 16.8新加入 hooks,新的逻辑复用方式(高阶组件/render props),hooks刚出来就开始使用,看了很多使用的教程,但是一直不清楚这里面的黑魔法是怎么实现的。
常用hook
useState/useEffect/useRef/useCallback/useMemo/useContext/useReducer
官方教程地址 react.docschina.org/docs/hooks-…
今天要实现的两个hook
实际上react内部是用的链表结构,这里为了简化,使用数组来模拟
useState
简要使用方法
useState接收一个参数初始值,返回值和修改函数,修改函数setState可以传值或者修改方法,当然useState也可以接收一个函数作为参数,函数返回值作为初始参数,这个比较简单就没有实现。
const [val,setVal]=useState(0);
实现
const stateCache = [];
let stateCursor = 0;
function useState(initialState) {
const currentCursor = stateCursor;
stateCache[currentCursor] = stateCache[currentCursor] || initialState; //是否初次渲染
function setState(value) {
stateCache[currentCursor] =
typeof value === "function" ? value(stateCache[currentCursor]) : value;
render(); //模拟框架内部render触发
}
stateCursor++;
return [stateCache[currentCursor], setState];
}
初始的状态和下标存储在全局变量里,状态值用数组存储,使用下标值获取,所以如果在判断和循环中使用,会导致下标错乱,示例中注视掉的部分,num1改变后,setNum2取到的就是原来testNum的下标和数据,setNum2也就不会生效了。
useEffect
简要使用说明
接收两个参数,第一个回调,第二个执行依赖,回调可以有返回值函数,在依赖有变化时执行,可以在里面做清理操作
useEffect(() => {
console.log("useEffect 执行,依赖num1");
return () => console.log("依赖num1清理函数执行");
}, [num1]);
实现
const effectCache = [];
let effectCursor = 0;
const effectCleanFnCache = [];
function useEffect(callback, deps) {
if (!Array.isArray(deps)) {
typeof effectCleanFnCache[effectCursor] === "function" &&
effectCleanFnCache[effectCursor]();
effectCleanFnCache[effectCursor] = callback();
effectCursor++;
return;
}
const currentCursor = effectCursor;
const depsBefore = effectCache[currentCursor];
if (!depsBefore) {
//初次渲染一定执行
effectCache[currentCursor] = deps;
effectCleanFnCache[effectCursor] = callback();
} else {
if (deps.some((v, i) => v !== depsBefore[i])) {
typeof effectCleanFnCache[effectCursor] === "function" &&
effectCleanFnCache[effectCursor]();
effectCleanFnCache[effectCursor] = callback();
}
}
effectCursor++;
}
完整示例代码
import React from "react";
import ReactDOM from "react-dom";
const stateCache = [];
let stateCursor = 0;
const effectCache = [];
let effectCursor = 0;
const effectCleanFnCache = [];
function useState(initialState) {
const currentCursor = stateCursor;
stateCache[currentCursor] = stateCache[currentCursor] || initialState; //是否初次渲染
function setState(value) {
stateCache[currentCursor] =
typeof value === "function" ? value(stateCache[currentCursor]) : value;
render(); //模拟框架内部render触发
}
stateCursor++;
return [stateCache[currentCursor], setState];
}
function useEffect(callback, deps) {
if (!Array.isArray(deps)) {
typeof effectCleanFnCache[effectCursor] === "function" &&
effectCleanFnCache[effectCursor]();
effectCleanFnCache[effectCursor] = callback();
effectCursor++;
return;
}
const currentCursor = effectCursor;
const depsBefore = effectCache[currentCursor];
if (!depsBefore) {
//初次渲染一定执行
effectCache[currentCursor] = deps;
effectCleanFnCache[effectCursor] = callback();
} else {
if (deps.some((v, i) => v !== depsBefore[i])) {
typeof effectCleanFnCache[effectCursor] === "function" &&
effectCleanFnCache[effectCursor]();
effectCleanFnCache[effectCursor] = callback();
}
}
effectCursor++;
}
function App() {
const [num1, setNum1] = useState(0);
// if (num1 === 0) {
// const [testNum] = useState("");
// }
const [num2, setNum2] = useState(0);
useEffect(() => {
console.log("useEffect 执行,依赖[]");
}, []);
useEffect(() => {
console.log("useEffect 执行,依赖空");
});
useEffect(() => {
console.log("useEffect 执行,依赖num1");
return () => console.log("依赖num1清理函数执行");
}, [num1]);
return (
<div>
<div>num1:{num1}</div>
<div>num2:{num2}</div>
<button onClick={() => setNum1(num1 + 1)}>add num1</button>
<button onClick={() => setNum2(num2 => num2 + 1)}>add num2</button>
</div>
);
}
/**
* 模拟react render
*/
function render() {
ReactDOM.render(<App />, document.getElementById("root"));
stateCursor = 0;
effectCursor = 0;
}
render();