React useEffect Hook


这个useEffectHook 允许您在组件中执行副作用。

副作用的一些示例包括:获取数据、直接更新 DOM 和计时器。

useEffect接受两个参数。第二个参数是可选的。

useEffect(<function>, <dependency>)


让我们以计时器为例。

例子:

使用setTimeout()初始渲染后计算 1 秒:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

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

  useEffect(() => {
    setTimeout(() => {
      setCount((count) => count + 1);
    }, 1000);
  });

  return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);

运行示例 »

可是等等!!即使只应计数一次,它仍会继续计数!

useEffect在每个渲染上运行。这意味着当计数发生变化时,就会发生渲染,然后触发另一个效果。

这不是我们想要的。有多种方法可以控制副作用何时发生。

我们应该始终包含接受数组的第二个参数。我们可以选择将依赖项传递给useEffect在这个数组中。

示例

1.没有传递依赖:

useEffect(() => {
  //Runs on every render
});

示例

2. 空数组:

useEffect(() => {
  //Runs only on the first render
}, []);

示例

3. 道具或状态值:

useEffect(() => {
  //Runs on the first render
  //And any time any dependency value changes
}, [prop, state]);

因此,为了解决这个问题,我们只在初始渲染上运行这个效果。

例子:

仅在初始渲染上运行效果:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

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

  useEffect(() => {
    setTimeout(() => {
      setCount((count) => count + 1);
    }, 1000);
  }, []); // <- add empty brackets here

  return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);

运行示例 »

例子:

这是一个例子useEffect依赖于变量的钩子。如果count变量更新后,效果会再次运行:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

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

  useEffect(() => {
    setCalculation(() => count * 2);
  }, [count]); // <- add the count variable here

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>+</button>
      <p>Calculation: {calculation}</p>
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Counter />);

运行示例 »

如果有多个依赖项,则应将它们包含在useEffect依赖数组。



效果清理

某些效果需要清理以减少内存泄漏。

应处理不再需要的超时、订阅、事件侦听器和其他效果。

我们通过在末尾包含一个返回函数来做到这一点useEffect钩。

例子:

结束时清理计时器useEffect钩:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

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

  useEffect(() => {
    let timer = setTimeout(() => {
    setCount((count) => count + 1);
  }, 1000);

  return () => clearTimeout(timer)
  }, []);

  return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);

运行示例 »

笔记:为了清除计时器,我们必须给它命名。


通过练习测试一下

练习:

您需要在第二个参数中添加什么useEffect挂钩将其限制为仅在第一次渲染时运行?

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    setData(getData())
  }, );

  return <DisplayData data={data} />;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

开始练习