闭包玩的明明白白
代码很简单,这里不做解读,这里重点说几点:
const React = (function(){
let hooks = [];
let idx = 0;
return {
render(component){
const C = component();
C.render();
idx=0;
return C;
},
useState(v){
const state = hooks[idx]||v;
let _idx = idx;
function setState(v) {
hooks[_idx] = v;
}
idx++;
return [state,setState];
},
}
}());
const {render,useState} = React;
function component(){
const [count,setCount] = useState(0);
const [fruits,setFruits] = useState('apple');
return {
render(){
console.log('渲染组件' ,count,fruits);},
clickCount(){
setCount(count+1);
},
clickFruits(v){
setFruits(v);
},
}
};
const com = render(component);
com.clickCount();
render(component);
com.clickCount();
render(component);
let com1 = render(component);
com1.clickFruits('app');
com1.clickCount();
render(component);
React 的 API 设计能力确实不错,用解构赋值将 state 和对应的 setState 放在一起,简洁明了;
useState 的第一次执行可以取代 class 的构造函数初始化过程,值为 useState 的参数 initVal,运行后存储在闭包中所对应的 hooks[index]
变量里。
从第二次 render 时开始访问 hooks[index] 而不是 initVal;
初始化时每调用一次 useState ,闭包里 hooks 便会递增分配对应的 index
key 来存储对应的值。render 结束后 index 会重置为 0,下一次 render
执行 useState 时会按照相同顺序访问 hooks[index];
useEffect
const React = (function () {
let hooks = [];
let idx = 0;
return {
render(Component) {
const C = Component(); // render effects
C.render();
idx = 0; // reset for next render
return C;
},
useState(initVal) {
const state = hooks[idx] || initVal;
const _idx = idx;
const setState = newVal => {
hooks[_idx] = newVal;
};
idx++;
return [state, setState];
},
useEffect(cb, depArray) {
const hasNoDeps = !depArray;//如果没有传第二个参数
hooks[idx] = hooks[idx] || {};
const { deps, cleanup } = hooks[idx]; // undefined when first render
const hasChanged = deps
? !depArray.every((el, i) => el === deps[i])
: true;
if (hasNoDeps || hasChanged) {
cleanup && cleanup();
hooks[idx].cleanup = cb();
hooks[idx].deps = depArray;
}
idx++;
},
};
})();
const { useState, useEffect, render } = React;
function Component() {
const [count, setCount] = useState(1);
const [text, setText] = useState("apple");
useEffect(() => {
console.log('effect', count);
return () => {
console.log('cleanup')
}
}, [count]);
return {
render() {
console.log("render", { count, text });
},
click() {
setCount(count + 1);
},
type(text) {
setText(text);
}
};
}
// every render
let App = render(Component);
App.click();
let App = render(Component);
App.type('pear');
let App = render(Component);