React useEffect
Hook
useEffect
Hook 允許您在元件中執行副作用。
一些副作用的例子是:獲取資料、直接更新 DOM 和計時器。
useEffect
接受兩個引數。第二個引數是可選的。
useEffect(<函式>, <依賴項>)
讓我們以計時器為例。
示例
使用 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. Props 或 state 值
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 />);
注意: 要清除計時器,我們必須為其命名。