本篇文章旨在用 最简单的代码 解释React Hook的 基础原理。
- UseState
const hooks = [];
let index = 0;
const useState = (initial) => {
const value = hooks[index] || initial;
const setValue = ((i) => (newVal) => {
if (hooks[i] === newVal) {
return
}
hooks[i] = newVal;
render();
})(index);
index += 1;
return [ value, setValue ];
}
function App() {
const [name, setName] = useState("apple");
//if (name === "tangerine") {
//const [other, setOther] = useState(10);
//console.log(`other ---> ${other}`)
//}
const [age, setAge] = useState(20);
setTimeout(() => {
setName("tangerine");
setAge(24);
}, 2000)
return `My Name Is ${name}, My Age Is ${age}`;
}
function render() {
// 重置index
index = 0;
document.body.innerHTML = App();
}
render();
执行以上代码,视图发生了变化My Name Is apple, My Age Is 20
--->
My Name Is tangerine, My Age Is 24
。
原理解析:利用数组存储了所有状态变量,index
代表的是变量在数组中的顺序。当调用setValue
方法改变状态值时,会重新执行整个组件函数,并在之前会重置index
为0
,原因在于重新执行组件函数时会再次依照状态变量的申明顺序去hooks
中去获取值。
基于以上,便可理解为何不能在条件语句中申明状态变量,如:
if (***) {
const [ sex ] = useState("male")
}
条件的变动会影响状态变量的顺序读值,即hooks
中的index
出现混乱。可将上述代码中注释部分放开,测试结果。
- UseEffect
const hooks = [];
const cbs = [];
let index = 0;
const useState = (initial) => {
const value = hooks[index] || initial;
const setValue = ((i) => (newVal) => {
if (hooks[i] === newVal) {
return
}
hooks[i] = newVal;
render();
})(index);
index += 1;
return [ value, setValue ];
}
const useEffect = (cb, deps) => {
if (!deps) {
cbs.push(cb);
return;
}
const lastDeps = hooks[index];
const isChanged = !lastDeps ? true : !lastDeps.every((item, index) => item === deps[index]);
if (isChanged) {
cbs.push(cb);
hooks[index] = deps;
}
index += 1;
}
function App() {
const [name, setName] = useState("apple");
const [age, setAge] = useState(20);
useEffect(() => {
console.log("no deps", document.body.innerHTML);
});
useEffect(() => {
console.log(`name is ${name}`)
}, [name]);
useEffect(() => {
console.log(`age is ${age}`)
}, [age])
setTimeout(() => {
setName("tangerine")
}, 2000);
return `My Name Is ${name}, My Age Is ${age}`;
}
function render() {
// 重置index
index = 0;
document.body.innerHTML = App();
while(cbs.length) {
cbs.splice(0, 1)[0]();
}
}
render();
执行以上代码,视图发生了变化My Name Is apple, My Age Is 20
-->
My Name Is tangerine, My Age Is 20
。控制台依次打印出no deps My Name Is apple, My Age Is 20
、name is apple
、age is 20
、no deps My Name Is tangerine, My Age Is 20
、name is tangerine
,可以发现useEffect
包裹的函数会在render
渲染完成之后执行一遍,对应生命周期componentDidMount
。当状态变量name
发生变化时,无依赖函数和依赖name
函数会执行。
原理解析:利用数组存储了useEffect
函数依赖项,执行useEffect
函数时,如果没有依赖项,将其参数函数放进队列;如果有依赖项,会找到其依赖项上次状态与此次状态加以比较,如果发生了变化,将其参数函数放进队列。在渲染完成后执行队列任务并 清空。
网友评论