Cancel a promise when a component is unmounted in ReactJS

不想你离开。 提交于 2019-12-23 06:58:52

问题


I've a component named "Item" which creates and calls a promise when it has been mounted.

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

As you can see, the promise calls the resolve 6 seconds after it has been called.

There is another component named "List" that is responsible for showing those items on the screen. The "List" is the parent of the "Item" component.

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)

On the example above, it shows three Item on the screen.

If I remove any of those components, componentWillUnmount() is called but also the promise which has been created in the removed component is run.

For example, I can see the promise of the second item is run even if I remove the second item.

unmounted 
PROMISE COMPLETED 1 
PROMISE COMPLETED 2 
PROMISE COMPLETED 3

I have to cancel the promise when a component is unmounted.


回答1:


A variation of this https://hshno.de/BJ46Xb_r7 seemed to work for me. I made an HOC with the mounted instance variable and wrapped all async components in it.

Below is what my code roughly loks like.

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



回答2:


You can't cancel native ES6 promises. Read more at https://medium.com/@benlesh/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

What you can do, however, is maybe use non-native promise libraries like Bluebird or Q, that give you promises that can be cancelled.




回答3:


Since you are using a timeout in this example you should clear it when unmounting.

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

My guess is this will result in a rejection and you won't see that console log.




回答4:


There are various things you can do. The simplest is to reject the promise:

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

and then

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

   console.log("unmounted")
}


来源:https://stackoverflow.com/questions/44457788/cancel-a-promise-when-a-component-is-unmounted-in-reactjs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!