React報錯之無法在未掛載的組件上執行React狀態更新
- 2022 年 8 月 3 日
- 筆記
正文從這開始~
總覽
為了解決"Warning: Can't perform a React state update on an unmounted component"
,可以在你的useEffect
鉤子中聲明一個isMounted
布爾值,用來跟蹤組件是否被安裝。一個組件的狀態只有在該組件被掛載時才會被更新。
import {useState, useEffect} from 'react';
const App = () => {
const [state, setState] = useState('');
useEffect(() => {
// 👇️ set isMounted to true
let isMounted = true;
async function fetchData() {
const result = await Promise.resolve(['hello', 'world']);
// 👇️ only update state if component is mounted
if (isMounted) {
setState(result);
}
}
fetchData();
return () => {
// 👇️ when component unmounts, set isMounted to false
isMounted = false;
};
}, []);
return (
<div>
<h2>State: {JSON.stringify(state)}</h2>
</div>
);
};
export default App;
當我們試圖更新一個未掛載的組件的狀態時,會出現”無法在未掛載的組件上執行React狀態更新”的警告。
isMounted
擺脫該警告的直截了當的方式是,在useEffect
鉤子中使用isMounted
布爾值來跟蹤組件是否被掛載。
在useEffect
中,我們初始化isMounted
布爾值為true
。
我們的fetchData
函數執行一些異步的任務,最常見的是一個API請求,並根據響應來更新狀態。
然而,需要注意的是,我們只有當isMounted
變量被設置為true
時,才會更新狀態。
async function fetchData() {
const result = await Promise.resolve(['hello', 'world']);
// 👇️ only update state if component is mounted
if (isMounted) {
setState(result);
}
}
這可以幫助我們避免警告,因為如果組件沒有掛載,我們就不會更新狀態。
當組件卸載時,從useEffect
鉤子返回的函數會被調用。
return () => {
// 👇️ when component unmounts, set isMounted to false
isMounted = false;
};
我們設置isMounted
變量為false
,表示該組件不再掛載。如果fetchData
函數在組件卸載時被調用,if
代碼塊不會執行是因為isMounted
設置為false
。
async function fetchData() {
const result = await Promise.resolve(['hello', 'world']);
// 👇️ only update state if component is mounted
if (isMounted) {
setState(result);
}
}
提取
如果經常這樣做,可以將邏輯提取到可重用的鉤子中。
import {useState, useEffect, useRef} from 'react';
// 👇️ extract logic into reusable hook
function useIsMounted() {
const isMounted = useRef(false);
useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
});
return isMounted;
}
const App = () => {
const [state, setState] = useState('');
// 👇️ use hook
const isMountedRef = useIsMounted();
useEffect(() => {
async function fetchData() {
const result = await Promise.resolve(['hello', 'world']);
// 👇️ only update state if component is mounted
if (isMountedRef.current) {
setState(result);
}
}
fetchData();
}, [isMountedRef]);
return (
<div>
<h2>State: {JSON.stringify(state)}</h2>
</div>
);
};
export default App;
useRef()
鉤子可以傳遞一個初始值作為參數。該鉤子返回一個可變的ref對象,其.current
屬性被初始化為傳遞的參數。
我們在useIsMounted
鉤子中跟蹤組件是否被掛載,就像我們直接在組件的useEffect
鉤子中做的那樣。
需要注意的是,在fetchData
函數中,我們必須檢查isMountedRef.current
的值,因為ref
上的current
屬性是ref
的實際值。