setState (…): Kann nur eine gemountete oder gemountete Komponente aktualisieren. Dies bedeutet normalerweise, dass Sie setState () für eine nicht gemountete Komponente aufgerufen haben. Dies ist ein No-Op

77
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');
}

Error:

Warnung: setState (...): Kann nur eine gemountete oder gemountete Komponente aktualisieren. Dies bedeutet normalerweise, dass Sie setState () für eine nicht gemountete Komponente aufgerufen haben. Dies ist ein No-Op. Bitte überprüfen Sie den Code für die undefinierte Komponente.

Ich entferneEventListener 'endete' in componentWillUnmount, aber es funktioniert nicht. weil ich hinzufügen this.setState({'time': remainTimeInfo});in componentDidMount.

useLess liang
quelle
Ich denke, das ist sehr ähnlich zu stackoverflow.com/questions/47923656/…
Eduard Jacko

Antworten:

98

Ich habe dieses Problem gelöst, indem ich der Komponente eine Referenz zugewiesen und dann überprüft habe, ob die Referenz vorhanden ist, bevor ich den Status festgelegt habe:

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

render() {
  return (
    <div ref="myRef">
      {this.state.myVar}
    </div>
  );
}
Tudor Morar
quelle
Sehr elegante Lösung!
Tenko
11
Danke dafür! Dies ist jedoch sehr erschwerend. Ziemlich alle meine Szenen machen versprechungsbasiertes Abrufen zum Laden und werfen diesen Fehler. Warum kann React nicht einfach stillschweigend ablehnen, setState in einem nicht gemounteten Zustand auszuführen? Es ist nicht so, dass irgendjemand den Unterschied sehen würde.
Mienaikoe
Fantastische Lösung !! So einfach und so effektiv!
Schmutz
Auch dies ist veraltet. Aber ist eine süße und einfache Lösung!
Pramesh Bajracharya
Warum kann ich nicht reagieren, um den Status festzulegen, da dies keine Rolle spielt?
PatS
23

removeEventListenerhat die gleiche Signatur wie addEventListener. Alle Argumente müssen exakt gleich sein, damit der Listener entfernt wird.

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

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

Und im Aufruf von componentWillUnmount this.cleanup().

Räuber
quelle
4

Ich bin auf dieses Problem gestoßen, weil ich es setStateanstelle des stateKonstruktors verwendet habe.

BEISPIEL

Ändern Sie den folgenden falschen Code

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

zu

constructor(props) {
  super(props);
  this.state = {
    key: ''
  }; 
}
Searene
quelle
Verdammt, ich habe das gerade aus Versehen gemacht. danke
user1189352
3

Bearbeiten : isMountedist veraltet und wird wahrscheinlich in späteren Versionen von React entfernt. Sehen Sie dies und das, isMounted ist ein Antipattern .


Wie in der Warnung angegeben, rufen Sie this.setStateeine Komponente auf, die bereitgestellt wurde , aber seitdem nicht mehr bereitgestellt wurde.

Um sicherzustellen, dass Ihr Code sicher ist, können Sie ihn einschließen

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

um sicherzustellen, dass die Komponente noch montiert ist.

Daniel B.
quelle
4
this.isMounted ist veraltet und löst nicht das Auslaufen von Ereignis-Listenern (weshalb es meistens veraltet ist)
Brigand
Vielen Dank, dass Sie darauf hingewiesen haben, aber warum wird es immer noch zum Laden von Anfangsdaten über AJAX verwendet ? Wo kann ich den aktuellen Status sehen?
Daniel B
isMounted ist ein Antipattern - facebook.github.io/react/blog/2015/12/16/…
Kolby
@Kolby, ja, meine Antwort erwähnt das.
Daniel B
3

Ich habe das gleiche Problem, seit ich die neueste Reaktionsversion aktualisiert habe. Wie unten gelöst.

Mein Code war

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

Gewechselt zu

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

Ich hatte dieses Problem schon einmal und löste es laut der offiziellen React-Seite isMounted is a Antipattern .

Setzen Sie ein Eigenschaftsflag so isMounted, dass es wahr ist componentDidMount, und schalten Sie es falsch ein componentWillUnmount. Wenn Sie setState()in Ihren Rückrufen, überprüfen Sie isMountedzuerst! Für mich geht das.

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

zurückrufen:

if (this.state.isMounted) {
 this.setState({'time': remainTimeInfo});}
user6042449
quelle
10
Das ist schlecht. Kein Grund zu speichern, isMountedum anzugeben. Sie sollten einfach verwenden this.isMounted. Dies hat nichts mit dem Reaktionszustand zu tun.
Sulthan
1
Außerdem können Sie this.setState nicht auf "componentWillUnmount" aufrufen
Marco Godínez
1
Vielleicht this._isMountedwiderspricht es auch nicht den veralteten isMounted()offiziellen React-Dokumentationen und wirft keine Warnungen vor veralteten Warnungen aus .
Cezar D.
Tatsächlich sollte ausstehender Code in componentWillUnmount abgebrochen werden. Dies ist immer noch das gleiche
Antimuster
1

Viele der Antworten in diesem Thread haben den Sinn, Refs zu verwenden, aber ich denke, ein vollständiges Beispiel wäre gut. Da Sie auf einem tatsächlichen DOM-Knoten arbeiten, indem Sie den Ereignis-Listener verwenden und den React-Kontext verlassen, sollte ein Verweis als Standardlösung betrachtet werden. Hier ist ein vollständiges Beispiel:

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)
  }
}
Zachary Skalko
quelle
1

addEventListener und removeEventListener , Der Callback darf keine anonyme innere Klasse sein und sollte dieselben Parameter haben

Mingo
quelle
2
Wie @FakeRainBrigand sagte, da ich keinen Ruf zum Kommentieren habe, möchte ich sagen: "Der Rückruf darf keine anonyme innere Klasse sein"
Mingo
Vielen Dank für den Hinweis, dass Rückruf keine anonyme Funktion sein sollte
Wasim Abuzaher
0

Ich erhielt diese Warnung, als ich ein Popup (Bootstrap-Modal) für einen erfolgreichen / fehlgeschlagenen Rückruf einer Ajax-Anfrage anzeigen wollte. Außerdem funktionierte setState nicht und mein Popup-Modal wurde nicht angezeigt.

Unten war meine Situation-

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

Ich habe die Ajax-Funktion der Komponente von der Enkelkindkomponente aufgerufen und einen onSuccess-Rückruf (definiert in der Enkelkindkomponente) übergeben, bei dem der Status so eingestellt wurde, dass das Popup-Modal angezeigt wird.

Ich habe es geändert in -

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

Stattdessen habe ich setState (onSuccess Callback) aufgerufen, um das Popup-Modal in der Komponente (Ajax Callback) selbst anzuzeigen und das Problem zu lösen.

Im zweiten Fall: Die Komponente wurde zweimal gerendert (ich hatte bundle.js zweimal in HTML eingefügt).

Varun Kumar
quelle
0

Wenn Sie die Methode anstelle der anonymen Funktion im audioNode.addEventListenerRückruf benannt haben, sollte die Betreff-Warnung entfernt werden:

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

Und ja, die benannte Methode wird sowieso benötigt, da removeEventListenersie nicht mit anonymen Rückrufen funktioniert, wie oben mehrmals erwähnt.

Vasiliy
quelle
0
  1. Brechen Sie alle asynchronen Vorgänge in ab componentWillUnmount
  2. Die Prüfkomponente ist beim asynchronen Aufruf bereits nicht gemountet setState,
    da das Flag isMounted veraltet ist
Shawn Wang
quelle