SWR在组件卸载后还会触发setState吗?

西门文科 阅读 44

我用SWR请求数据,但在组件卸载后控制台报了警告,说不能在已卸载的组件上执行setState。是不是SWR没处理好取消逻辑?

我试过在useEffect里加了abort controller,但SWR好像有自己的处理机制。下面是我的代码:

import useSWR from 'swr';

function MyComponent() {
  const { data, error } = useSWR('/api/user', fetcher);

  if (error) return <div>failed to load</div>;
  if (!data) return <div>loading...</div>;

  return <div>{data.name}</div>;
}
我来解答 赞 11 收藏
二维码
手机扫码查看
2 条解答
ლ心霞
ლ心霞 Lv1
这个问题确实挺常见的。SWR 本身是有取消逻辑的,通常情况下不会在组件卸载后还触发 setState。不过有时候还是会出现这种警告,可能是由于一些特殊情况。

你提到的 abort controller 是一个解决这类问题的好方法,不过 SWR 也提供了一些内置的方式来处理这种情况。你可以尝试使用 SWR 的 useSWRImmutable 或者手动控制 revalidateOnFocusrevalidateOnReconnect 这些选项来避免不必要的重新验证。

不过最简单直接的解决办法,还是利用 SWR 提供的 mutate 函数的返回值,它会返回一个 promise,我们可以在组件卸载时取消这个 promise。具体可以这样做:

import useSWR, { mutate } from 'swr';

function MyComponent() {
const { data, error } = useSWR('/api/user', fetcher);
let cancelRequest = null;

useEffect(() => {
const promise = mutate('/api/user', fetcher);
cancelRequest = () => promise.cancel();

return () => {
if (cancelRequest) {
cancelRequest();
}
};
}, []);

if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;

return <div>{data.name}</div>;
}


这样,在组件卸载的时候,我们可以主动取消掉未完成的请求,从而避免在已卸载的组件上调用 setState。

希望这样能解决你的问题,有时候这些库自带的方法就能完美解决,真是又爱又恨呢。
点赞
2026-03-22 04:00
シ天琪
シ天琪 Lv1
这锅不一定是SWR的。SWR确实会自动取消请求,但前提是你的fetcher得听指挥。组件卸载时,SWR会发一个AbortSignal,如果你的fetcher收到信号还在继续跑,最后把数据吐回来,SWR就会尝试更新状态,这时候组件早没了,自然报警告。

简单改一下fetcher,加上signal的处理逻辑就行。SWR 2.0+ 会自动把 signal 传给 fetcher。

const fetcher = async (url, { signal } = {}) => {
// 把 signal 传给 fetch,这样 abort 时请求会直接中断
const res = await fetch(url, { signal });

if (!res.ok) {
const error = new Error('An error occurred while fetching the data');
error.info = await res.json();
error.status = res.status;
throw error;
}

return res.json();
};


如果你用的是axios,逻辑也差不多,把signal塞进去。或者如果你不想折腾fetcher,最简单的办法是用SWR官方提供的中间件或者直接用原生fetch,别自己瞎封装,这种坑就少了。

还有一种情况,是你自己在useEffect里监听data变化然后setState,那个记得在useEffect里加个unmounted标记,不过看你的代码应该不是这个问题。先改fetcher试试。
点赞 1
2026-03-04 19:19