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-useuseAsync 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]);

如您所见,钩子为您维护loadingerror 值,无需执行任何操作,因此您可以专注于实际功能。如果您的 async 函数实际上返回了您想要使用的某个值,则该值将放置在名为 value 的属性中,如果 loading 为 false 且 error 没有值,您可以使用该属性。

请记住,您传递给 useAsync 钩子的异步函数也不应该执行任何状态更新,否则您仍会收到相同的错误消息。

【讨论】:

当我这样做时,应用程序甚至没有启动,只是给我一个错误Can't find variable: document 很遗憾,我猜是因为某种原因它与 React Native 不兼容?我一直在 Web 项目中使用它。我确实看到该库不再维护,这真是令人遗憾。 我添加了 useAsync 钩子的实现,它应该也可以在 React Native 中使用 非常感谢您解决了这个问题 - 我感谢您所做的努力! 没问题,很高兴为您提供帮助!

以上是关于React Native - 无法对未安装的组件执行 React 状态更新,使用Effect 清理功能的主要内容,如果未能解决你的问题,请参考以下文章

无法对未安装的组件执行 React 状态更新。内存泄漏

React useEffect 抛出错误:无法对未安装的组件执行 React 状态更新

警告:无法对未安装的组件(多个组件)执行 React 状态更新

反应钩子。无法对未安装的组件执行 React 状态更新

错误:无法对未安装的组件执行 React 状态更新

无法使用 fetch POST 方法对未安装的组件执行 React 状态更新