web-dev-qa-db-fra.com

setState (...): Ne peut mettre à jour qu'un composant monté ou un composant de montage. Cela signifie généralement que vous avez appelé setState () sur un composant non monté. C'est un no-op

componentDidMount(prevProps, prevState, prevContext) {
    let [audioNode, songLen] = [this.refs.audio, List.length-1];

    audioNode.addEventListener('ended', () => {
        this._endedPlay(songLen, () => {
            this._currSong(this.state.songIndex);
            this._Play(audioNode);
        });
    });

    audioNode.addEventListener('timeupdate', () => {
        let [remainTime, remainTimeMin, remainTimeSec, remainTimeInfo] = [];

        if(!isNaN(audioNode.duration)) {
            remainTime = audioNode.duration - audioNode.currentTime;
            remainTimeMin = parseInt(remainTime/60);  // 剩余分
            remainTimeSec = parseInt(remainTime%60);  // 剩余秒

            if(remainTimeSec < 10) {
                remainTimeSec = '0'+remainTimeSec;
            }
            remainTimeInfo = remainTimeMin + ':' + remainTimeSec;
            this.setState({'time': remainTimeInfo});
        }
    });
}

componentWillUnmount () {
    let audio = this.refs.audio;
    audio.removeEventListener('timeupdate');
    audio.removeEventListener('ended');
}

Erreur:

Attention: setState (...): ne peut mettre à jour qu'un montage ou un montage composant. Cela signifie généralement que vous avez appelé setState () sur un .mounted composant. C'est un no-op. S'il vous plaît vérifier le code pour le indéfini composant.

Je retireEventListener 'terminé' dans componentWillUnmount, mais cela ne fonctionne pas. parce que j'ajoute this.setState({'time': remainTimeInfo}); dans componentDidMount.

73
useLess liang

J'ai résolu ce problème en affectant un ref au composant, puis en vérifiant si le ref existe avant de définir l'état:

myMethod(){
  if (this.refs.myRef) 
   this.setState({myVar: true});
}

render() {
  return (
    <div ref="myRef">
      {this.state.myVar}
    </div>
  );
}
96
Tudor Morar

removeEventListener a la même signature que addEventListener. Tous les arguments doivent être exactement les mêmes pour supprimer l'auditeur.

var onEnded = () => {};
audioNode.addEventListener('ended', onEnded, false);

this.cleanup = () => {
  audioNode.removeEventListener('ended', onEnded, false);
}

Et dans le composantWillUnmount, appelez this.cleanup().

23
FakeRainBrigand

J'ai rencontré ce problème car j'ai utilisé setState au lieu de state dans le constructeur.

EXEMPLE

Changer le code incorrect suivant

constructor(props) {
  super(props);
  this.setState({
    key: ''
  });
}

à

constructor(props) {
  super(props);
  this.state = {
    key: ''
  }; 
}
4
Searene

Edit: isMounted est obsolète et sera probablement supprimé des versions ultérieures de React. Voir this et this, isMounted est un Antipattern .


Comme l'indique l'avertissement, vous appelez this.setState sur un composant que was a monté mais a depuis été démonté. 

Pour vous assurer que votre code est sécurisé, vous pouvez l'envelopper 

if (this.isMounted()) {
    this.setState({'time': remainTimeInfo});
}

pour vous assurer que le composant est toujours monté.

3
Daniel B

J'ai rencontré le même problème depuis que j'ai mis à jour la dernière version de Rea. Résolu comme ci-dessous.

Mon code était 

async componentDidMount() {
  const { default: Component } = await importComponent();
  Nprogress.done();
  this.setState({
    component: <Component {...this.props} />
  });
}

Changé en 

componentWillUnmount() {
  this.mounted = false;
}
async componentDidMount() {
  this.mounted = true;
  const { default: Component } = await importComponent();
  if (this.mounted) {
    this.setState({
      component: <Component {...this.props} />
    });
  }
}
2
Jaison

J'avais ce problème auparavant et je l'ai résolu conformément à la page officielle de React isMounted est un Antipattern .

Définissez un indicateur de propriété isMounted sur true dans componentDidMount et basculez-le sur false dans componentWillUnmount. Lorsque vous setState() dans vos rappels, vérifiez d'abord isMounted! Ça marche pour moi.

state = {
    isMounted: false
  }
  componentDidMount() {
      this.setState({isMounted: true})
  }
  componentWillUnmount(){
      this.setState({isMounted: false})
  }

rappeler:

if (this.state.isMounted) {
 this.setState({'time': remainTimeInfo});}
2
user6042449

Beaucoup de réponses dans ce fil de discussion ont le sens d'utiliser des références, mais je pense qu'un exemple complet serait bien. Puisque vous travaillez sur un nœud DOM réel en utilisant l'écouteur d'événements et en sortant du contexte React, une référence doit être considérée comme la solution standard. Voici un exemple complet:

class someComponent extends Component {
  constructor(props) {
    super(props)
    this.node = null
  }
  render() {
    return (
      <div ref={node => { this.node = node }}>Content</div>
    )
  }
  handleEvent(event) {
    if (this.node) {
      this.setState({...})
    }
  }
  componentDidMount() {
    //as soon as render completes, the node will be registered.
    const handleEvent = this.handleEvent.bind(this)
    this.node.addEventListener('click', handleEvent)
  }
  componentWillUnmount() {
    const handleEvent = this.handleEvent.bind(this)
    this.node.removeEventListener('click', handleEvent)
  }
}
1
Zachary Skalko

addEventListener et removeEventListener le rappel ne doit pas être une classe interne anonyme, et ils doivent avoir les mêmes paramètres

1
Mingo

Avoir nommé la méthode à la place de la fonction anonyme dans le rappel de audioNode.addEventListener devrait éliminer l'avertissement du sujet:

    componentDidMount(prevProps, prevState, prevContext) {
    let [audioNode, songLen] = [this.refs.audio, List.length-1];

    audioNode.addEventListener('ended', () => {
        this._endedPlay(songLen, () => {
            this._currSong(this.state.songIndex);
            this._Play(audioNode);
        });
    });

    audioNode.addEventListener('timeupdate', this.callbackMethod );
}

callBackMethod = () => {
    let [remainTime, remainTimeMin, remainTimeSec, remainTimeInfo] = [];

    if(!isNaN(audioNode.duration)) {
        remainTime = audioNode.duration - audioNode.currentTime;
        remainTimeMin = parseInt(remainTime/60);  // 剩余分
        remainTimeSec = parseInt(remainTime%60);  // 剩余秒

        if(remainTimeSec < 10) {
            remainTimeSec = '0'+remainTimeSec;
        }
        remainTimeInfo = remainTimeMin + ':' + remainTimeSec;
        this.setState({'time': remainTimeInfo});
    }
}

Et oui, la méthode nommée est de toute façon nécessaire car removeEventListener ne fonctionnera pas avec les rappels anonymes, comme mentionné ci-dessus à plusieurs reprises.

0
Vasiliy

Je recevais cet avertissement lorsque je voulais afficher une fenêtre contextuelle (modal bootstrap) lors du rappel de réussite/échec de la demande Ajax. De plus, setState ne fonctionnait pas et ma modale popup n'était pas affichée. 

Ci-dessous ma situation

<Component /> (Containing my Ajax function)
    <ChildComponent />
        <GrandChildComponent /> (Containing my PopupModal, onSuccess callback)

J'appelais la fonction ajax du composant petitchild en passant un callback onSuccess (défini dans le composant petitchild) qui configurait state pour afficher le mode modal. 

Je l'ai changé pour -

<Component /> (Containing my Ajax function, PopupModal)
    <ChildComponent />
        <GrandChildComponent /> 

Au lieu de cela, j'ai appelé setState (onSuccess Callback) pour afficher le composant modal dans le composant (rappel ajax) lui-même et le problème résolu.

Dans le second cas: le composant était rendu deux fois (j'avais inclus bundle.js deux fois en html). 

0
Varun Kumar
  1. Annuler toutes les opérations asynchrones dans componentWillUnmount
  2. Le composant de vérification est déjà démonté lors de l'appel asynchrone setState,
    Depuis que l'indicateur isMounted est obsolète
0
Shawn Wang