web-dev-qa-db-fra.com

Annuler une promesse lorsqu'un composant est démonté dans ReactJS

J'ai un composant nommé "Item" qui crée et appelle une promesse quand il a été monté.

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

Comme vous pouvez le voir, la promesse appelle la résolution 6 secondes après son appel.

Il existe un autre composant nommé "Liste" qui est chargé d'afficher ces éléments à l'écran. La "Liste" est le parent du composant "Article".

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)

Dans l'exemple ci-dessus, il montre trois éléments à l'écran.

enter image description here

Si je supprime l'un de ces composants, componentWillUnmount () est appelé mais également la promesse qui a été créée dans le composant supprimé est exécutée.

Par exemple, je peux voir que la promesse du deuxième élément est exécutée même si je supprime le deuxième élément.

unmounted 
PROMISE COMPLETED 1 
PROMISE COMPLETED 2 
PROMISE COMPLETED 3

Je dois annuler la promesse lorsqu'un composant est démonté.

14
amone

Une variation de cela https://hshno.de/BJ46Xb_r7 semblait fonctionner pour moi. J'ai créé un HOC avec la variable d'instance mounted et j'ai encapsulé tous les composants asynchrones.

Voici à quoi ressemble mon code.

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);
3
Rohan Bagchi

Vous ne pouvez pas annuler les promesses natives d'ES6. En savoir plus sur https://medium.com/@benlesh/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

Cependant, vous pouvez peut-être utiliser des bibliothèques de promesses non natives comme Bluebird ou Q , qui vous donnent des promesses qui peuvent être annulé.

1
Chitharanjan Das

Vous pouvez faire différentes choses. Le plus simple est de reject la promesse:

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

puis

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

   console.log("unmounted")
}
1
Sulthan

Étant donné que vous utilisez un délai d'expiration dans cet exemple, vous devez le supprimer lors du démontage.

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

Je suppose que cela entraînera un rejet et vous ne verrez pas ce journal de console.

0
valem