我正在尝试下面的useEffect示例:
useEffect(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
setPosts(json.data.children.map(it => it.data));
} catch (e) {
console.error(e);
}
}, []);
我在控制台得到这个警告。但我认为,对于异步调用,清理是可选的。我不知道为什么我得到这个警告。链接沙盒为例。https://codesandbox.io/s/24rj871r0p
其他答案已经有很多例子给出了,并且解释得很清楚,所以我将从TypeScript类型定义的角度来解释它们。
useEffect钩子TypeScript签名:
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
效果类型:
// NOTE: callbacks are _only_ allowed to return either void, or a destructor.
type EffectCallback = () => (void | Destructor);
// Destructors are only allowed to return void.
type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never };
现在我们应该知道为什么effect不能是一个异步函数了。
useEffect(async () => {
//...
}, [])
async函数将返回一个带有隐式未定义值的JS promise。这不是useEffect的期望。
正确执行并避免错误:“警告:无法在未挂载的…上执行React状态更新…”
useEffect(() => {
let mounted = true;
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
})();
return () => {
mounted = false;
};
}, []);
或外部函数和使用对象
useEffect(() => {
let status = { mounted: true };
query(status);
return () => {
status.mounted = false;
};
}, []);
const query = async (status: { mounted: boolean }) => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
if (status.mounted) {
setPosts(newPosts);
}
} catch (e) {
console.error(e);
}
};
或中止控制器
useEffect(() => {
const abortController = new AbortController();
(async () => {
try {
const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`, { signal: abortController.signal });
const json = await response.json();
const newPosts = json.data.children.map(it => it.data);
setPosts(newPosts);
} catch (e) {
if(!abortController.signal.aborted){
console.error(e);
}
}
})();
return () => {
abortController.abort();
};
}, []);
在React提供更好的方法之前,你可以创建一个帮助器useEffectAsync.js:
import { useEffect } from 'react';
export default function useEffectAsync(effect, inputs) {
useEffect(() => {
effect();
}, inputs);
}
现在你可以传递一个async函数:
useEffectAsync(async () => {
const items = await fetchSomeItems();
console.log(items);
}, []);
更新
如果您选择这种方法,请注意这是一种糟糕的形式。当我知道这是安全的时候,我就会这样做,但这总是糟糕的方式和随意的。
悬疑的数据获取,仍处于实验阶段,将解决一些情况。
在其他情况下,您可以将异步结果建模为事件,以便您可以根据组件生命周期添加或删除侦听器。
或者你可以将异步结果建模为一个Observable,这样你就可以根据组件的生命周期订阅和取消订阅。