web-dev-qa-db-fra.com

composantDidMount: impossible d'appeler setState (ou forceUpdate) sur un composant non monté

Je récupère des données dans composantDidMount et met à jour l'état et l'avertissement célèbre apparaît:

Avertissement: Impossible d'appeler setState (ou forceUpdate) sur un composant non monté. Ceci est un non-op, mais cela indique une fuite de mémoire dans votre application. Pour résoudre ce problème, annulez toutes les souscriptions et les tâches asynchrones dans la méthode ComponentWillUnmount.

Mon code est le suivant:

componentDidMount() {
    let self = this;

    let apiBaseUrl = Config.serverUrl;
    axios.get( apiBaseUrl + '/dataToBeFetched/' )
        .then( function(response) {
            self.setState( { data: response.data } );;
        } );
}

Quelle est la cause de cet avertissement et quel est le meilleur moyen d'extraire les données et de mettre à jour l'état?

5
O_k

Sur la base d’une réponse précédente, j’ai effectué les opérations suivantes, qui ont bien fonctionné:

constructor(props) {
   this.state = {isMounted: false}
}

componentDidMount() {
    let apiBaseUrl = Config.serverUrl;
    this.setState( { isMounted: true }, () => {
        axios.get( apiBaseUrl + '/dataToBeFetched/' )
            .then( (response) => { // using arrow function ES6
                if( this.state.isMounted ) {
                    this.setState( { pets: response.data } );
                }
            } ).catch( error => {
                // handle error
        } )
    } );
}

componentWillUnmount() {
    this.setState( { isMounted: false } )
}

Une autre meilleure solution consiste à annuler la demande lors du démontage de la manière suivante:

constructor(props) {
    this._source = axios.CancelToken.source();
}

componentDidMount() {
    let apiBaseUrl = Config.serverUrl;
    axios.get( apiBaseUrl + '/dataToBeFetched/', { cancelToken: this._source.token } )
        .then( (response) => { // using arrow function ES6
            if( this.state.isMounted ) {
                this.setState( { pets: response.data } );
            }
        } ).catch( error => {
            // handle error
    } );
}

componentWillUnmount() {
    this._source.cancel( 'Operation canceled due component being unmounted.' )
}
6
O_k

Vous pouvez essayer ceci:

constructor() {
    super();
    this._isMounted = false;
}

componentDidMount() {
    this._isMounted = true;
    let apiBaseUrl = Config.serverUrl;
    this.setState( { isMounted: true }, () => {
        axios.get( apiBaseUrl + '/dataToBeFetched/' )
            .then( (response) => { // using arrow function ES6
                if( this._isMounted ) {
                    this.setState( { pets: response.data } );
                }
            } ).catch( error => {
                // handle error
        } )
    } );
}

componentWillUnmount() {
    this._isMounted = false; // equals, not :
}
1
user10942985

Cela est probablement dû au fait que le composant était déjà démonté avant la fin de l'appel asynchrone. Cela signifie que votre appel à setState dans la promesse axios est appelé après le démontage du composant, peut-être à cause d'une redirection react-router ou d'un changement d'état?

0
alexlod

Appeler setState sur componentWillUnmount est la pire pratique

componentWillUnmount() {
    this.setState( { isMounted: false } ) // don't do this
}
0
Shawn Wang