避免在 unmounted 组件上更新 state,因为这是无意义的:
export default Component = () => {
const [ data, setData ] = useState(null);
useEffect(() => {
(async () {
setData(await someAsyncApiCall());
})();
}, []);
return (<span>{ data }</span>);
}
如果 someAsyncApiCall()
返回数据时,组件已经被 unmount 的话,React 会在控制台输出警告:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
这篇 帖子 给了一个解决方法,使用一个 mount 变量来跟踪组件的生命周期:
export default Component = () => {
const [ data, setData ] = useState(null);
useEffect(() => {
let mounted = true; // Indicate the mount state
(async () {
const data = await someAsyncApiCall();
if (!mounted) return; // Exits if comp. is not mounted
setData(data);
})();
return () => { // Runs when component will unmount
mounted = false;
};
}, []);
return (<span>{ data }</span>);
}
mounted
变量不能是定义在 useEffect()
外。不然会报这样的错误:
Assignments to the 'mounted' variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect.
但如果你有在 useEffect
外使用 mounted 变量的需求,你可以用 useRef
来传递一个 mutable value。比如下面的组件:
- 在组件 mount 时发起 API 请求,加载笔记列表
- 另外一个函数发起 API 请求,新增一条笔记
它们都需要考虑组件是否 mount:
import React, { useEffect, useRef, useState } from "react"
import api from "utils/api"
export default function Home () {
const [notes, setNotes] = useState([])
let mountedRef = useRef(true)
useEffect(() => {
api.get("/notes")
.then(data => {
if (!mountedRef.current) {
return
}
setNotes(data)
})
return () => {
mountedRef.current = false
}
},
[]
)
const handleCreatingNewNote = (e) => {
api.post("/notes", { content: content })
.then(data => {
if (!mountedRef.current) {
return
}
setNotes(prevNotes => {
return [data, ...prevNotes]
})
})
}
return (
// ...
)
}