-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React 18 可中断 mount 导致 useRequest 有 cacheKey 时更新报错 #1866
Comments
|
这个 issue 的问题主要不是 useUpdate 引起的 而我是觉得既然 React 存在 render/update 可中断的的情况,那么 useUpdate 作为一个对外的 hook,也应该考虑对这种情况的兼容 保证 update() 执行是在被中断的情况下,是否应该有容错呢?因为这种在 unmount 的 Component 中进行更新产生了报错是 React 的渲染机制导致的,不是用户的不规范编码导致的 这个可以讨论看下,我也不确定这种报错是否可以算作是用户的编码不规范导致的,ahook useUpdate 不作容错,让 React 正常报错 搞了个 Demo 试了下直接在 Component 内使用是没事,但放在另一个 hook 内的复杂场景是会出错的,如上面的 useRequest
import { useCallback, useEffect, useRef, useState } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { useUpdate, useMount, useCreation } from 'ahooks';
const DocsPage = () => {
const countRef = useRef(0);
console.log('🚀 ~ file: index.tsx ~ line 7 ~ countRef pre', countRef.current);
countRef.current = countRef.current + 1;
console.log('🚀 ~ file: index.tsx ~ line 10 ~ countRef after', countRef.current);
const data = useCustom(
new Promise((requestBack) => {
setTimeout(() => {
requestBack('requestData');
}, 1500);
})
);
useEffect(() => {
console.log('useEffect [] 是只执行了一次');
}, []);
console.time('模拟耗时的 render ');
let n = 0;
while (true) {
window.getComputedStyle(document.body);
n++;
if (n > 999999) break;
}
console.timeEnd('模拟耗时的 render ');
return (
<PageContainer>
<p>This is umi docs.</p>
</PageContainer>
);
};
function useCustom(service: PromiseLike<string>) {
const update = useUpdate();
const dataRef = useRef<string | null>(null);
useCreation(() => {
service.then((data) => {
dataRef.current = data;
update();
});
}, []);
}
export default DocsPage; |
这个报错是正常的,React18 已经解释过,这个报错是可以忽略的。 看下这个:brickspert/blog#47 |
brickspert/blog#47 这个链接与上面的问题在给出的处理结果上可能是一致的(允许在 unmounted 的组件 setState) 但具体可能还是不太一样,官方说的是 mount 后 unmount 引起 setState,上面的是 mount 中断后重新 mount 引起的第一个已经没意义的 mount fiber 中的 setState,像提到的以后允许在 umounted 的组件可以正常 setState,但这种未正常 mount 的组件我觉得应该不在这个范围吧 而上面提到的报错可能还是会令人感到困惑,毕竟那个只是告警都作了移除以避免误导,而这个错误还是在 [email protected] 中 不清楚官方对这个有没有计划,或许这个错误该放到 React 18 中去讨论 👀 |
这个ahook是不是考虑,升级一下,现在的性能确实稍差,比起swr还是有些不如, 主要还是缓存那块,问题有些大啊 |
问题:
页面组件异步加载时,在挂载过程触发了其他高优先级任务(如下图鼠标滑到上面的tab触发 hover),打断当前的 mount 过程
原因:
mount可中断导致 useRef 会重新初始化,进而导致被中断 mount 的 Fiber hook useUpdate 的 update 函数被重复构造的 fetchInstance = useCreation() 收集,最终在 cacheSubscribe 监听无法取消(因为没有 mount 不会触发 unmount )
hooks/packages/hooks/src/useCreation/index.ts
Lines 5 to 17 in a902010
update 函数被 重复 new Fetch 收集
hooks/packages/hooks/src/useRequest/src/useRequestImplement.ts
Lines 25 to 36 in a902010
hooks/packages/hooks/src/useRequest/src/Fetch.ts
Lines 18 to 23 in a902010
hooks/packages/hooks/src/useRequest/src/Fetch.ts
Lines 31 to 37 in a902010
然后被会话变量 cacheSubscribe.subscribe 注册(每次的 Fetch 实例都会被注册到一个内存变量中,直到 unmount 或刷新页面)
hooks/packages/hooks/src/useRequest/src/plugins/useCachePlugin.ts
Lines 114 to 116 in a902010
hooks/packages/hooks/src/useRequest/src/utils/cacheSubscribe.ts
Line 2 in a902010
hooks/packages/hooks/src/useRequest/src/utils/cacheSubscribe.ts
Lines 10 to 20 in a902010
debugger log:
综上,在被中断 mount 时,此次更新的 fiber 的 强制渲染 update 函数随着 Fetch 实例 被注册到一个内存变量中
{ cacheKey: [] }
,在下次正常 mount 完成请求后触发对应 cacheKey 的所有 Fetch 实例更新导致 update 更新在一个 hasn't mounted component测试仓库: https://github.com/zjfresh/umi4-umi-max
The text was updated successfully, but these errors were encountered: