有没有办法检查反应组件是不是已卸载?

Posted

技术标签:

【中文标题】有没有办法检查反应组件是不是已卸载?【英文标题】:Is there a way to check if the react component is unmounted?有没有办法检查反应组件是否已卸载? 【发布时间】:2017-02-07 14:42:46 【问题描述】:

我有一个用例需要卸载我的反应组件。但在某些情况下,特定的 react 组件会被不同的函数卸载。 因此,我需要在卸载之前检查组件是否已安装。

【问题讨论】:

【参考方案1】:

建议您使用 useRef 挂钩来跟踪组件是否已安装,因为每当您更新状态时,react 都会重新渲染整个组件以及它将触发 useEffect 或其他钩子的执行。

function MyComponent(props: Props) 
  const isMounted = useRef(false)

  useEffect(() => 
    isMounted.current = true;
    return () =>  isMounted.current = false 
  , []);

  return (...);


export default MyComponent;

然后检查组件是否使用if (isMounted.current) ... 挂载

【讨论】:

对类组件使用***.com/a/39767963/1783174。将其用于钩子。 你的意思是说你推荐使用useState而不是“不”?因为您的解决方案似乎使用它 如果您在单个组件中有多个 useEffects,则将调用清理函数,因此这不起作用,因为重新渲染和重新安装之间没有区别。 “另外,如果一个组件渲染多次(通常是这样),前一个效果会在执行下一个效果之前被清除。” reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect【参考方案2】:

使用@DerekSoike 回答,但是在我的情况下,使用useState 来控制挂载状态不起作用,因为状态在不需要时复活了

对我有用的是使用单个变量

myFunctsetTimeout中被调用,我的猜测是,当同一个组件在另一个页面中初始化钩子时,它恢复了导致内存泄漏再次出现的状态

所以这对我不起作用

  const [isMounted, setIsMounted] = useState(false)

  useEffect(() => 
    setIsMounted(true)
    return () => setIsMounted(false)
  , [])

  const myFunct = () => 
    console.log(isMounted) // not always false
    if (!isMounted) return
    // change a state
  

这确实对我有用

  let stillMounted =  value: false 
  useEffect(() => 
    stillMounted.value = true
    return () => (stillMounted.value = false)
  , [])

  const myFunct = () => 
    if (!stillMounted.value) return
    // change a state
  

【讨论】:

这有效,而之前的策略(在许多地方更新)没有...谢谢@GWorking 如果我们将 isMounted 状态更新为 true 或 false,它将重新加载功能组件,我建议使用 let 变量。【参考方案3】:

如果你使用钩子:

function MyComponent(props: Props) 
  const [isMounted, setIsMounted] = useState<boolean>(false);

  useEffect(() => 
    setIsMounted(true);
  , []);

  useEffect(() => 
    return () => 
      setIsMounted(false);
    
  , []);

  return (...);


export default MyComponent;

【讨论】:

这篇文章dev.to/trentyang/replace-lifecycle-with-hooks-in-react-3d4n 将有助于提供更多关于如何从 React Native Class 风格转换为 React Hooks 风格的细节 @ZiaUlRehmanMughal 第一个useEffect 挂钩在组件挂载时调用一次,因为一个空数组作为依赖项列表传递。第二个useEffect 钩子传递了一个清理函数,该函数在卸载时被调用。查看文档了解更多详情:reactjs.org/docs/hooks-reference.html#useeffect - 这是内存泄漏。您正在对已卸载的组件调用 setState。我建议人们使用其他answer【参考方案4】:

由于isMounted() 已被正式弃用,您可以在您的组件中执行此操作:

componentDidMount()  
  this._ismounted = true;


componentWillUnmount() 
   this._ismounted = false;

这种维护自己的state 变量的模式在 ReactJS 文档中详细说明:isMounted is an Antipattern。

【讨论】:

感谢来自文档的提示...只需在 componentDidMount 中将 _isMounted 属性设置为 true 并在 componentWillUnmount 中将其设置为 false ...但是,使用 this.setState(mounted:错误的);可能为时已晚而无法阻止警告,因为状态更改不会立即发生 - 最好为此使用类属性 ... this.mounted = false - 谢谢,这为我指明了正确的方向 @danday74,是的,你是对的。当我写答案时,我可能错过了重点。如果有帮助,请考虑为答案投票 @KevinGhaboosi,不幸的是,只有提出这个问题的人才能接受这个答案【参考方案5】:

你可以使用:

myComponent.updater.isMounted(myComponent)

"myComponent" 是你的反应组件的实例。 如果组件已安装,则返回“true”,如果未安装,则返回“false”..

这是不支持的方法。你最好取消订阅任何异步/事件 在 componentWillUnmount 上。

【讨论】:

谢谢,这有助于我进行调试。【参考方案6】:

相同的想法,但另一个实现

/**
 * component with async action within
 * 
 * @public
 */
class MyComponent extends Component 
    constructor ( ...args ) 
        // do not forget about super =)
        super(...args);
        // NOTE correct store "setState"
        let originSetState = this.setState.bind(this);
        // NOTE override
        this.setState = ( ...args ) => !this.isUnmounted&&originSetState(...args);
    
    /**
     * no necessary setup flag on component mount
     * @public
     */
    componentWillUnmount() 
        // NOTE setup flag
        this.isUnmounted = true;
    
    /**
     *
     * @public
     */
    myCustomAsyncAction () 
        // ... code
        this.setState(any: 'data'); // do not care about component status
        // ... code
    

    render ()  /* ... */ 

【讨论】:

【参考方案7】:

另一种解决方案是使用 Refs 。如果您使用的是 React 16.3+,请在渲染函数中对您的***项目进行引用。

然后简单地检查 ref.current 是否为空。

例子:

class MyClass extends React.Component 
  constructor(props) 
    super(props);
    this.elementRef = React.createRef();
  

  checkIfMounted() 
     return this.elementRef.current != null;
  

  render() 
    return (
      <div ref=this.elementRef />
    );
  

【讨论】:

【参考方案8】:

我来到这里是因为我正在寻找一种方法来阻止 polling API。

react docs 确实涵盖了 websocket 案例,但不包括轮询案例。

我的工作方式

// React component

React.createClass(
    poll () 
        if (this.unmounted) 
            return
        
        // otherwise, call the api
    
    componentWillUnmount () 
        this.unmounted = true
    
)

它有效。希望对你有帮助

如果你们知道任何失败的测试用例,请告诉我=]

【讨论】:

它可以工作,但是如果你有多个实例呢? all 将引用相同的 allowPolling 是的,你最好不过在实例中,我将编辑代码 sn-p。【参考方案9】:

我认为 Shubham 的回答是 react 为需要转换代码以停止使用 isMounted 反模式的人们建议的解决方法。

这不一定是坏事,但值得列出这个问题的真正解决方案。

The article linked by Shubham 提供 2 条建议来避免这种反模式。您需要的取决于您在卸载组件时调用 setState 的原因。

如果您在组件中使用 Flux 存储,则必须在 componentWillUnmount 中取消订阅

class MyComponent extends React.Component 
  componentDidMount() 
    mydatastore.subscribe(this);
  
  render() 
    ...
  
  componentWillUnmount() 
    mydatastore.unsubscribe(this);
  

如果您使用 ES6 Promise,您可能需要包装您的 Promise 以使其可取消。

const cancelablePromise = makeCancelable(
  new Promise(r => component.setState(...))
);

cancelablePromise
  .promise
  .then(() => console.log('resolved'))
  .catch((reason) => console.log('isCanceled', reason.isCanceled));

cancelablePromise.cancel(); // Cancel the promise

在链接文章中了解更多关于 makeCancelable 的信息。

总之,不要试图通过设置变量和检查组件是否已挂载来修补此问题,而是要找到问题的根源。如果您能提出任何其他常见案例,请发表评论。

【讨论】:

想象一个帖子列表。每个帖子都是一个单独的组件,并且旁边(内部)有一个删除按钮。当用户点击按钮时,post 设置isDeleting = true(禁用按钮),告诉父组件删除帖子(使用通过 props 传递的回调),当他完成时(另一个回调,但到 post 组件)它可能需要启用按钮。例如。发生 HTTP 错误时(帖子尚未删除)。也就是说,post 组件可能需要根据它是否仍然挂载来更改状态。这有什么问题吗? 只是为了指出仍然来这里的人。参考文章中的makeCancelable 方法是设置一个局部变量——就像Shubham 的例子一样。没有办法真正取消 Promise。我很难理解为什么在其他地方设置一个布尔值比在 React 组件中清楚地设置它更好。不过,关于取消订阅的观点是有效的。 我的组件在对象的属性上设置了代理。当该对象更新时,代理会调用setState,以便属性和组件的状态同步。所以在这种情况下,这些建议是无关紧要的。【参考方案10】:

我发现组件会被卸载,生成填充这个var

if(!this._calledComponentWillUnmount)this.setState(vars);

【讨论】:

这是一个内部属性,我认为与它们一起玩并不安全,因为它将来可能会改变。 好吧,老实说,在你更新库之前它是安全的,所以在某些情况下,这是一个很好的解决方案。但应该有一个条件!== undefined 以确保该属性存在。 当然,最好还是听从官方的建议。

以上是关于有没有办法检查反应组件是不是已卸载?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法检查延迟加载的组件(使用 React.Lazy)是不是已完成加载?

卸载组件时对 setState 做出反应警告

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

反应原生如何检查组件当前是不是显示在屏幕中

当我导航到本机反应的新页面时,如何强制卸载组件?

样式化组件和反应引导?