官网:如果我的 effect 的依赖频繁变化,我该怎么办?问题解析

419 阅读2分钟

问题引出

有时候,你的 effect 可能会使用一些频繁变化的值。你可能会忽略依赖列表中 state,但这通常会引起 Bug:

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // 这个 effect 依赖于 `count` state   
      }, 1000);
    return () => clearInterval(id);
  }, []); // 🔴 Bug: `count` 没有被指定为依赖
  return <h1>{count}</h1>;
}

引发的问题:

  1. 只会在初始化的时候执行一次

知识点:使用Effect Hook,通过跳过Feect进行性能优化

如果想执行并运行一次的effect(仅在组件挂载和卸载时执行),可以传递一个空数组[]作为第二个参数,这就是告诉React你的effect不依赖于props或state中的任何值,所以它永远不会重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。

  1. count的值永远不会超过1

因为当effect执行时,我们回创建一个闭包,并将count的值保存在该闭包内,且初始值为0,每隔一秒,回调就会执行setCount(0 + 1),所以count 永远不会超过 1。

  • Q 如果这个时候增加一个button,来强制更改count的值呢?

在一秒之后count会重新赋值为1,即还是取闭包内的count,进行+1

import React,{ useEffect, useState } from 'react';
import ReactDOM from 'react-dom';

function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
    const id = setInterval(() => {
    setCount(count + 1); // 这个 effect 依赖于 `count` state
    }, 1000);
    return () => clearInterval(id);
}, []); // 🔴 Bug: `count` 没有被指定为依赖
return (
    <div>
        <h1>{count}</h1>
        <button onClick={()=>{setCount(count+1)}}>+1</button>
    </div>
    )
}
ReactDOM.render(
    <Counter />, document.getElementById('container'),
);

如果这个时候,将[]改为count呢?

还会有其他问题

每次改变时定时器都会被重置。事实上,每个 setInterval 在被清除前(类似于 setTimeout)都会调用一次。但这并不是我们想要的。

知识点:useEffect会在调用一个新的effect之前对前一个effect进行清理,如下面例子

image.png

要解决这个问题,可以使用setState的函数式进行更新

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1); // ✅ 在这不依赖于外部的 `count` 变量    
    }, 1000);
    return () => clearInterval(id);
  }, []); // ✅ 我们的 effect 不适用组件作用域中的任何变量
  return <h1>{count}</h1>;
}

这种写法,可取到Count的实时数值