React useEffect
Hooks
The useEffect
Hook 允许你在组件中执行副作用。
一些副作用的例子包括:获取数据、直接更新 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
Hook 的示例。如果 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
Hook 的末尾包含一个返回函数来做到这一点。
示例
在 useEffect
Hook 的末尾清理计时器
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 />);
注意:为了清除计时器,我们必须为其命名。