取消 componentWillUnmount 方法中的所有 Subscriptions 和 Asyncs,如何?

Posted

技术标签:

【中文标题】取消 componentWillUnmount 方法中的所有 Subscriptions 和 Asyncs,如何?【英文标题】:Cancel All Subscriptions and Asyncs in the componentWillUnmount Method, how? 【发布时间】:2019-02-03 07:12:15 【问题描述】:

由于异步方法问题,我收到一条错误消息。在我的终端中,我看到了:

Warning: Can't call setState (or forceUpdate) 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 the componentWillUnmount method.
- node_modules/fbjs/lib/warning.js:33:20 in printWarning
- node_modules/fbjs/lib/warning.js:57:25 in warning
- node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js:12196:6 in warnAboutUpdateOnUnmounted
- node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js:13273:41 in scheduleWorkImpl
- node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js:6224:19 in enqueueSetState
- node_modules/react/cjs/react.development.js:242:31 in setState
* router/_components/item.js:51:16 in getImage$
- node_modules/regenerator-runtime/runtime.js:62:44 in tryCatch
- node_modules/regenerator-runtime/runtime.js:296:30 in invoke
- ... 13 more stack frames from framework internals

我注意到它特别指出了getImage$

这是我用于该部分的代码:

export default class extends Component 
    constructor(props) 
        super(props);
        const  item  = props

        const bindThese =  item 
        this.boundActionCreators = bindActionCreators(bindThese)

        this.state = 
            image: require('../../static/logo.png'),
            ready: false,
            showOptions: this.props.showOptions
        

        this.getImage = this.getImage.bind(this)
        this.renderNotAdmin = this.renderNotAdmin.bind(this)
        this.renderAdmin = this.renderAdmin.bind(this)
        this.handleOutOfStock = this.handleOutOfStock.bind(this)
    

    async getImage(img) 
        let imgUri = await Amplify.Storage.get(img)
        let uri = await CacheManager.get(imgUri).getPath()

        this.setState(
            image:  uri ,
            ready: true
        )
    

    componentDidMount() 
        this.getImage(this.props.item.image)
    

我试图弄清楚如何将componentWillUnmount 与这种异步方法一起使用。我该怎么办?

谢谢!

【问题讨论】:

是什么导致了这个错误?只是加载组件是吗?你也可以发布render 方法吗? 【参考方案1】:

我通过覆盖setState 方法解决了错误

isSubscribed = true;

componentWillUnmount() 
  this.isSubscribed = false;


setState = (state, callback?) => 
   if (this.isSubscribed) 
     super.setState(state, callback);
   

【讨论】:

【参考方案2】:

您可以在这里使用isMounted React 模式来避免内存泄漏。

在你的构造函数中:

constructor(props) 
    super(props);

    this._isMounted = false;
// rest of your code


componentDidMount() 
    this._isMounted = true;
    this._isMounted && this.getImage(this.props.item.image);

在你的componentWillUnmount

componentWillUnmount() 
   this._isMounted = false;

当你在getImage()

async getImage(img) 
    let imgUri = await Amplify.Storage.get(img)
    let uri = await CacheManager.get(imgUri).getPath()

    this._isMounted && this.setState(
        image:  uri ,
        ready: true
    )

使用基于可取消承诺模式的 Axios 的推荐方法。因此,您可以在使用cancelToken subscription 卸载组件时取消任何网络调用。 这是Axios Cancellation的资源

【讨论】:

如果组件在网络请求开始后和结束前卸载,您如何取消。 xhr 请求有自己的AbortController。 Axios 使用它的令牌作为信号,我们在我们的 axios API 调用中传递它。在任何时候,我们都可以通过 `axios.CancelToken.source().cancel('API is Cacnelled') 取消带有令牌的待处理请求。它很棒而且功能强大,而且在我们的应用程序中没有留下任何警告。在清除 redux 商店一次时,我玩得很开心。但它就像一个魅力 @SakhiMansoor 你说得非常清楚!就像一个魅力,我只是在以下位置添加了一个下划线:this._isMounted && this.getImage(this.props.item.image); 难以置信,感谢您提供 axios 取消链接。谢谢 但是如果我使用 React Hooks,我该怎么做呢?我写了这个:hastebin.com/omihuhurag.js,但它仍然给我同样的错误。 :(【参考方案3】:

需要在componentWillUnmount()方法中设置this.mounted = false,在componentDidMount()方法中设置this.mounted = true。

setState 更新是基于条件的,需要在 componentDidMount() 方法中声明。

componentDidMount() 
        this.mounted = true;
        var myVar =  setInterval(() => 
                let nextPercent = this.state.percentage+10;
                if (nextPercent >= 100) 
                    clearInterval(myVar);
                
                if(this.mounted) 
                    this.setState( percentage: nextPercent );
            
        , 100);


componentWillUnmount()
      this.mounted = false;

【讨论】:

【参考方案4】:

来自React blog

只需在 componentDidMount 中设置一个 _isMounted 属性为 true 并设置它 在 componentWillUnmount 中设置为 false,并使用此变量检查您的 组件的状态。

接着说,理想情况下,这将通过使用可取消回调来解决,尽管第一个解决方案似乎适合这里。

绝对不应该使用 isMounted() 函数,该函数可能已被弃用。

【讨论】:

以上是关于取消 componentWillUnmount 方法中的所有 Subscriptions 和 Asyncs,如何?的主要内容,如果未能解决你的问题,请参考以下文章

取消 componentWillUnmount 方法中的所有 Subscriptions 和 Asyncs,如何?

我如何在反应中取消订阅

在已卸载的组件中发出有关setState的警告

clearInterval 在 componentWillUnmount 中不起作用

useEffect 模拟 componentWillUnmount 不返回更新状态

P20:React高级-生命周期讲解 componentWillUnmount