react setState调用为什么会触发整个组件函数的重新运行
例如下面的代码
function App(){const [age,setAge]=useState(5);const clickHandler=()=>{setAge(5)}console.log("点击div时候,我也会被刷新")return (<div onClick={clickHandler}><div>)
}
那调用setAge时候到底发生了什么呢?
setAge是从useState里面解构出来的,那我们把关注点赚到useState里面。
setAge执行会导致App函数重新执行,但是age还是保存之前的结果!!
这说明
useState很可能一个闭包函数,执行后返回了个数组[state,setSate];
useState带代码可能如此:
const map=[];
const index=0;
const useState=(initValue)=>{index++;const setState=()=>{}return [map[index],setState]
}
APP函数重新运行
然后setState的执行会触发执行它的函数的重新运行,这个怎么写呢?
caller
当当当~~~~~~~~
Function.prototype.caller出场!!!!!!
大家都用过this,那个对象的方法调用我们的函数,我们的this就指向那个对象
那函数也有个属性,它在那个方法内执行,caller就指向那个函数的内存地址!!
代码改成如此:
const map=[];
const index=0;
const useState=(initValue)=>{index++;const caller=useState.caller;const setState=()=>{//重新执行App函数caller()}return [map[index],setState]
}
上面代码里面的index每次调用的时候都会递增,那setState调用时候会触发App的调用那index不是还是会继续递增么?如果App里面有多个useState,那就有问题了
这样无法做到index和我们useState的调用顺序保持一致!!!
那我们在caller()前面让index=0;重新赋值为0,这样就可以了
const map=[];
const index=0;
const useState=(initValue)=>{index++;const caller=useState.caller;const setState=()=>{//重新执行App函数index=0;caller()}return [map[index],setState]
}
这里的index申明是否可以省略呢,其实也可以,但是这样就得手动给App函数添加index属性,Function是Object的实现,所以它也可以有自己的属性。
完整代码改成如下:
在线链接:JS Bin - Collaborative JavaScript Debugging
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button id="btn">add</button><p>输出结果<span id="txt">-</span></p><script>//react 内部源码///演示为什么age变化适合,App函数里面的其他打印回会执行// var index = 0;//不需要了var map = [];const stateFn = {};function useState(init) {let caller = useState.caller;useState.caller.index++;let index = useState.caller.index;map[index] = map.hasOwnProperty(index) ? map[index] : init;const setState = (val) => {if (typeof val === 'function') {let currV = map[index];map[index] = val(currV);}else {map[index] = val}//重置App 函数对象的index索引caller.index = 0;//重新执行App函数caller()return map[index];}// stateFn.setState = setState;return [init, setState]} //react 组件 function App() {console.log("!!!!!调用setAge会触发App函数重新执行!!!!!!")const [age, setAge] = useState(0);const [year, setYear] = useState(2022);const btn = document.getElementById("btn");const txt = document.getElementById("txt");txt.innerHTML = `age:${age} year:${year}`;btn.onclick = () => {let ageResult = setAge((val) => val + 1);let yearResult = setYear((val) => val + 1);txt.innerHTML = `age:${ageResult} year:${yearResult}`;}}//正常写代码是不需要这样写的,可能编译器可以给他添加上去App.index = 0;//export default App; App()</script>
</body></html>