React Native - 无法对未安装的组件执行 React 状态更新,使用Effect 清理功能
Posted
技术标签:
【中文标题】React Native - 无法对未安装的组件执行 React 状态更新,使用Effect 清理功能【英文标题】:React Native - Can't perform a React state update on an unmounted component, useEffect cleanup function 【发布时间】:2021-10-26 09:06:46 【问题描述】:我知道已经有很多关于这方面的问题,但我已经尝试了所有我能找到的解决方案,但都没有奏效。我正在使用返回图像的 react-redux 执行 API 请求。第一次执行良好,但如果我返回导航堆栈,然后返回执行请求的位置,我会收到以下警告:
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.
组件中出现这种情况的代码是:
const image = useSelector((state) => state.images.image);
const dispatch = useDispatch();
const loadImage = useCallback(async () =>
setError(null);
setIsLoading(true);
try
await dispatch(actions.fetchImage());
catch (err)
setError(err.message);
setIsLoading(false);
, [dispatch]);
useEffect(() =>
setIsLoading(true);
loadImage().then(() =>
setIsLoading(false);
);
, [setIsLoading, loadImage]);
我找到的最有希望的解决方案是这样,但这并没有摆脱错误:
useEffect(() =>
let mounted = true;
setIsLoading(true);
loadImage().then(() =>
if (mounted)
setIsLoading(false);
);
return function cleanup()
mounted = false;
;
, [setIsLoading, loadImage]);
有什么想法吗?
【问题讨论】:
【参考方案1】:第一个观察结果是您只是取消了 useEffect
中的设置器,而不是 loadImage
中的设置器。
众所周知,具有(状态)效果的异步函数很棘手。虽然您可能可以使用您提出的解决方案来解决它,但它会使您的代码更难阅读和维护。
使用异步钩子
我建议使用来自react-use
的useAsync
hook
不幸的是,正如你所想,这个库与 React Native 不兼容。
我冒昧地创建了一个基本的 useAsync
钩子,它应该对您的用例足够有用,并且应该在 React Native 中工作(我手头没有项目可以测试它):
type UseAsyncResult<R> =
| loading: false; error: undefined; value: R
| loading: boolean; error: any; value: undefined ;
function useAsync<T extends () => Promise<R>, R>(
fn: T,
deps: unknown[]
): UseAsyncResult<R>
const mounted = useRef(true);
const [state, setState] = useState<UseAsyncResult<R>>(
loading: true,
error: undefined,
value: undefined
);
useEffect(() =>
return () =>
mounted.current = false;
;
, []);
useEffect(() =>
fn()
.then((value) =>
if (mounted.current)
setState( loading: false, error: undefined, value );
)
.catch((error) =>
if (mounted.current)
setState( loading: false, error, value: undefined );
);
, deps);
return state;
它使用您在解决方案中提到的布尔值来查看组件是否仍然挂载,并且仅在挂载时执行状态更改。
使用useAsync
,您的代码将变为:
const image = useSelector((state) => state.images.image);
const dispatch = useDispatch();
const loading, error = useAsync(() => dispatch(actions.fetchImage()), [dispatch]);
如您所见,钩子为您维护loading
和error
值,无需执行任何操作,因此您可以专注于实际功能。如果您的 async 函数实际上返回了您想要使用的某个值,则该值将放置在名为 value
的属性中,如果 loading
为 false 且 error
没有值,您可以使用该属性。
请记住,您传递给 useAsync
钩子的异步函数也不应该执行任何状态更新,否则您仍会收到相同的错误消息。
【讨论】:
当我这样做时,应用程序甚至没有启动,只是给我一个错误Can't find variable: document
很遗憾,我猜是因为某种原因它与 React Native 不兼容?我一直在 Web 项目中使用它。我确实看到该库不再维护,这真是令人遗憾。
我添加了 useAsync
钩子的实现,它应该也可以在 React Native 中使用
非常感谢您解决了这个问题 - 我感谢您所做的努力!
没问题,很高兴为您提供帮助!以上是关于React Native - 无法对未安装的组件执行 React 状态更新,使用Effect 清理功能的主要内容,如果未能解决你的问题,请参考以下文章
React useEffect 抛出错误:无法对未安装的组件执行 React 状态更新