闭包指的是那些引用了另一个函数作用域中变量的函数

  1. function createIncrement() {
  2. let value = 0;
  3. return function increment() {
  4. console.log(++value);
  5. }
  6. }
  7. // 1.创建函数
  8. let inc = createIncrement();
  9. // 2.调用
  10. inc(); // 1
  11. inc(); // 2
  12. // 3.销毁, 设置为null会解除对函数的引用,从而让垃圾回收程序释放掉内存
  13. inc = null

React Hooks闭包问题

useEffect注册时,会将所有已定义变量获取一遍,当做状态值。获取的second值是0,所以匿名箭头函数能访问到的状态second值为0,所以如果执行setSecond(second+1)用的second一直是状态值0,而不是最新的second。

  1. const [second, setSecond] = useState(0);
  2. useEffect(() => {
  3. let timer = setInterval(() => {
  4. // 这时候的second由于闭包的原因,一直是0,所以只能看到second变化一次。
  5. setSecond(second+1);
  6. }, 1000)
  7. }, [])
  8. // 模拟实现
  9. // function effect(){
  10. // return (fun:Function) => {
  11. // fun(...arguments)
  12. // }
  13. // }
  14. // const useEffect = effect({second:0})
  15. // useEffect(({second})=>{
  16. // for (let i = 0; i<5; i++){
  17. // const num = second + 1
  18. // console.log('改正前',num)
  19. // } // 1 1 1 1 1
  20. // let num = second
  21. // for (let i = 0; i<5; i++){
  22. // ++num
  23. // console.log('改正后',num)
  24. // } // 1 2 3 4 5
  25. // })

解决方案1:

重新定义一个状态值,改变这个状态值

  1. const [second, setSecond] = useState(0);
  2. useEffect(() => {
  3. let num = second
  4. let timer = setInterval(() => {
  5. num+=1 //不能直接second+=1,因为second没法直接更改值
  6. setSecond(num);
  7. }, 1000)
  8. }, [])

解决方案2:

setXxx接收的如果是个函数,会自动将最新的state当做函数参数。

  1. const [second, setSecond] = useState(0);
  2. useEffect(() => {
  3. let timer = setInterval(() => {
  4. // n 也就是最新的second值
  5. setSecond(n => {return n + 1});
  6. }, 1000)
  7. }, [])