如何检查组件是不是已卸载到功能组件中?

Posted

技术标签:

【中文标题】如何检查组件是不是已卸载到功能组件中?【英文标题】:How can I check if the component is unmounted in a functional component?如何检查组件是否已卸载到功能组件中? 【发布时间】:2019-12-28 15:31:54 【问题描述】:

回调函数设置组件状态。但有时提供数据的订阅需要结束。因为回调是异步执行的,所以它不知道订阅是否在服务调用(它执行回调函数)之后结束。

然后我在控制台中看到以下错误:

警告:无法对未安装的组件执行 React 状态更新。 这是一个空操作,但它表明您的应用程序中存在内存泄漏。 要修复,请取消 useEffect 中的所有订阅和异步任务 清理功能。

有没有办法访问组件状态,即使我在回调函数中?

这将是步骤:

带参数订阅 退订 组件已卸载 订阅的服务执行回调函数 回调函数在未安装的组件中设置状态,并在上面给出错误

【问题讨论】:

我猜你使用hooks useState 你可以使用useEffect (错误提示)返回一个cleanup function 来改变一个mounted variable 并检查that variable before calling the useState function 【参考方案1】:

你可以使用这样的引用:

const mounted = useRef(false);

useEffect(() => 
    mounted.current = true;

    return () =>  mounted.current = false; ;
, []);

然后在你的回调中你可以检查mounted.current === false 并避免设置状态

【讨论】:

非常干净和简单的解决方案!【参考方案2】:

这里有一些伪代码,您可以使用useEffect 查看组件是否已挂载。

它使用 useEffect 来侦听someService,当它收到消息时它会检查组件是否已挂载(组件卸载时也会调​​用清理函数),如果是,则使用由useState 创建的setServiceMessage设置服务接收的消息:

import  useState, useEffect  from 'react';
import someService from 'some-service';

export default props => 
  const userId = props.userId;
  const [serviceMessage, setServiceMessage] = useState([]);
  useEffect(
    () => 
      const mounted =  current: true ;
      someService.listen(
        //listen to messages for this user
        userId, 
        //callback when message is received
        message => 
          //only set message when component is mounted
          if (mounted.current) 
            setServiceMessage(serviceMessage.concat(message));
          
      );
      //returning cleanup function
      return () => 
        //do not listen to the service anymore
        someService.stopListen(userId);
        //set mounted to false if userId changed then mounted
        //  will immediately be set to true again and someService
        //  will listen to another user's messages but if the 
        //  component is unmounted then mounted.current will 
        //  continue to be false
        mounted.current = false;
      ;
    ,//<-- the function passed to useEffects
    //the function passed to useEffect will be called
    //every time props.userId changes, you can pass multiple
    //values here like [userId,otherValue] and then the function
    //will be called whenever one of the values changes
    //note that when this is an object then hi:1 is not hi:1
    //referential equality is checked so create this with memoization
    //if this is an object created by mapStateToProps or a function
    [userId]
  );
;

【讨论】:

【参考方案3】:

我发现这个问题的公认答案很难阅读,React 提供了他们自己的documentation on just this question。他们的例子是:

import React,  useState, useEffect  from 'react';

function FriendStatus(props) 
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => 
    function handleStatusChange(status) 
      setIsOnline(status.isOnline);
    
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Specify how to clean up after this effect:
    return function cleanup() 
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    ;
  );

  if (isOnline === null) 
    return 'Loading...';
  
  return isOnline ? 'Online' : 'Offline';

我创建了一个名为&lt;Fade&gt; 的组件,它将淡入/淡出它给定的任何子元素。请注意,它依赖于 bootstrap 的 .fade.show 类,尽管这些可以在没有 bootstrap 的情况下轻松实现。

const Fade: React.FC<> = ( children ) => 
  const [ className, setClassName ] = useState('fade')
  const [ newChildren, setNewChildren ] = useState(children)

  useEffect(() => 
    setClassName('fade')

    const timerId = setTimeout(() => 
      setClassName('fade show')
      setNewChildren(children)
    , TIMEOUT_DURATION)

    return () => 
      clearTimeout(timerId)
       

  , [children])

  return <Container fluid className=className + ' p-0'>newChildren</Container>

这一切都归结为一条规则:useEffect返回的清理函数中取消订阅您的异步任务

【讨论】:

【参考方案4】:

您可以从 useEffect 返回一个函数,该函数将在卸载功能组件时触发。 Please read this

import React,  useEffect  from 'react';
const ComponentExample = () => 
    useEffect(() => 
        // Anything in here is fired on component mount.
        return () => 
            // Anything in here is fired on component unmount.
        
    , [])

【讨论】:

【参考方案5】:

这个钩子(来自 Mohamed 的回答)以更优雅的方式解决了这个问题:

function useMounted() 
  const mounted = useMemo(() => ( current: true ), []);
  useEffect(() => 
    return () =>  mounted.current = false
  , [mounted]);
  return mounted;

(更新为使用 useMemo 而不是 useRef 以提高可读性)。

【讨论】:

这个解决方案看起来很干净,但我不明白 useRef 在这种情况下的用户。 我已经更新了代码示例。它不再使用 useRef。

以上是关于如何检查组件是不是已卸载到功能组件中?的主要内容,如果未能解决你的问题,请参考以下文章

如何彻底删除sql server已安装实例

如何检查 Laravel 刀片组件是不是已加载?

如何将状态值从一个组件访问到位于单独文件中的其他功能(不是组件)?反应js

当组件属性取决于当前日期时间时,如何管理Angular2“检查后表达式已更改”异常

加载组件时检查道具是不是未定义

如何测试子组件是不是已呈现?