在 componentWillUnmount 方法中取消所有订阅和异步,如何?

IT技术 javascript node.js reactjs react-native lifecycle
2021-03-25 19:54:26

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

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)
    }

我正在尝试弄清楚如何将 acomponentWillUnmount与这种异步方法一起使用我该怎么做?

谢谢!

4个回答

你可以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
    })
}

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

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

来自React 博客

只需在 componentDidMount 中将 _isMounted 属性设置为 true 并在 componentWillUnmount 中将其设置为 false,然后使用此变量检查组件的状态。

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

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

您需要在 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;
}

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

isSubscribed = true;

componentWillUnmount() {
  this.isSubscribed = false;
}

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