在 ReactJS 中卸载组件时取消承诺

Posted

技术标签:

【中文标题】在 ReactJS 中卸载组件时取消承诺【英文标题】:Cancel a promise when a component is unmounted in ReactJS 【发布时间】:2017-11-11 11:18:47 【问题描述】:

我有一个名为“Item”的组件,它在挂载后创建并调用一个 Promise。

class Item extends React.Component
    constructor(props)
        super(props)
        this.onClick = this.onClick.bind(this)

        this.prom = new Promise((resolve, reject) => 
            setTimeout(() => resolve("PROMISE COMPLETED "+this.props.id),6000)
        )
    

    componentDidMount()
        this.prom.then((success) => 
            console.log(success)
        )
    

    componentWillUnmount()
       console.log("unmounted")
    

    onClick(e)
        e.preventDefault()
        this.props.remove(this.props.id)
    

    render()
        return (
            <h1>Item this.props.id - <a href="#" onClick=this.onClick>Remove</a></h1>
        )
    

如您所见,promise 在调用 6 秒后调用 resolve。

还有另一个名为“List”的组件负责在屏幕上显示这些项目。 “List”是“Item”组件的父级。

class List extends React.Component
    constructor(props)
        super(props)
        this.state = 
            items : [1,2,3]
        

        this.handleRemove = this.handleRemove.bind(this)
    

    handleRemove(id)
        this.setState((prevState, props) => (
            items : prevState.items.filter((cId) => cId != id)
        ));
    

    render()
        return (
            <div>
            this.state.items.map((item) => (
                <Item key=item id=item remove=this.handleRemove  />
            ))
            
            </div>
        )
    


ReactDOM.render(<List />,root)

在上面的例子中,它在屏幕上显示了三个项目。

如果我删除了这些组件中的任何一个,componentWillUnmount() 将被调用,但已在已删除组件中创建的 promise 也会运行。

例如,即使我删除了第二项,我也可以看到第二项的承诺仍在运行。

unmounted 
PROMISE COMPLETED 1 
PROMISE COMPLETED 2 
PROMISE COMPLETED 3

卸载组件时我必须取消承诺。

【问题讨论】:

我认为我们无法检查这个答案:***.com/questions/30233302/…,也请检查这个:esdiscuss.org/topic/cancellation-architectural-observations 您是否只尝试在this.prom 调用Promise 构造函数一次? 不确定我是否完全理解这个问题,但this fiddle 有帮助吗?第 8 行和第 28 行是要查看的行 【参考方案1】:

https://hshno.de/BJ46Xb_r7 的一个变体似乎对我有用。 我用mounted 实例变量创建了一个HOC,并将所有异步组件包装在其中。

下面是我的代码大致的样子。

export function makeMountAware(Component) 
    return class MountAwareComponent extends React.Component 
        mounted = false;
        componentDidMount() 
            this.mounted = true;
        
        componentWillUnmount() 
            this.mounted = false;
        
        return (
            <Component 
                mounted = this.mounted
                ...this.props
                ...this.state
            />
        );
    


class AsyncComponent extends React.Component 
    componentDidMount() 
        fetchAsyncData()
            .then(data => 
                this.props.mounted && this.setState(prevState => (
                    ...prevState,
                    data
                ));
            );
    

export default makeMountAware(AsyncComponent);

【讨论】:

这不会导致内存泄漏,因为即使组件被卸载,promise 仍然持有对它的引用?【参考方案2】:

您不能取消原生 ES6 承诺。阅读更多https://medium.com/@benlesh/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

但是,您可以做的是使用非原生的 promise 库,例如 Bluebird 或 Q,它们会为您提供可以取消的 promise。

【讨论】:

***.com/questions/29478751/… js 承诺只是承诺,不管使用什么库,@chitharanjan-das,你的意思是说原生 js 承诺不能被取消吗?【参考方案3】:

您可以做很多事情。最简单的就是rejectpromise:

this.prom = new Promise((resolve, reject) => 
     this.rejectProm = reject;
     ...
);

然后

componentWillUnmount()
   if (this.rejectProm) 
      this.rejectProm();
      this.rejectProm = nil;
   

   console.log("unmounted")

【讨论】:

【参考方案4】:

由于您在此示例中使用了超时,因此您应该在卸载时将其清除。

class Item extends React.Component
    constructor(props)
        super(props)
        this.onClick = this.onClick.bind(this)

        // attribute for the timeout
        this.timeout = null;

        this.prom = new Promise((resolve, reject) => 
          // assign timeout
          this.timeout = setTimeout(() => resolve("PROMISE COMPLETED "+this.props.id),6000)
        )
    

    componentDidMount()
        this.prom.then((success) => 
            console.log(success)
        )
    

    componentWillUnmount()
       // clear timeout
       clearTimeout(this.timeout);
       console.log("unmounted")
    

我的猜测是这将导致拒绝,并且您不会看到该控制台日志。

【讨论】:

以上是关于在 ReactJS 中卸载组件时取消承诺的主要内容,如果未能解决你的问题,请参考以下文章

是否有一个函数返回一个承诺,当在 React Native 中应用了对组件的任何未决更改时,该承诺得到解决?

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

在 ReactJS 中显示/隐藏组件

React进行数据处理

React进行数据处理

如何在清理函数中的 React js 中执行 fileReader.removeEventListener