web-dev-qa-db-fra.com

Impossible d'appeler setState sur un composant non encore monté

c'est la première fois que je fais face à ce message d'avertissement.

Impossible d'appeler setState sur un composant non encore monté.

Suit:

Ceci est un non-op, mais cela pourrait indiquer un bogue dans votre application. Attribuez plutôt à this.state directement ou définir un state = {}; Propriété de classe avec l'état souhaité dans le composant MyComponent.

La partie " pas encore montée " n'a en réalité que peu de sens car le seul moyen de déclencher le problème est d'appeler un fonction en cliquant sur un bouton d'un composant qui doit être monté afin de voir le bouton. Le composant n'est pas démonté à un moment donné non plus.

Ce composant factice reproduit l'erreur dans mon application:

import PropTypes from 'prop-types'
import React from 'react'

export default class MyComponent extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      initial: 'state'
    }
    this.clickMe = this.clickMe.bind(this)
  }

  clickMe () {
    this.setState({
      some: 'new state'
    })
  }

  render () {
    return (
      <div>
        <button onClick={this.clickMe}>click</button>
      </div>
    )
  }
}

J'utilise:

"react": "16.3.2",
"react-dom": "16.3.2",
"mobx": "4.2.0",
"mobx-react": "5.1.2",

Ai-je oublié quelque chose dans la dernière version de React/mobx? (notez que le composant n'utilise aucun élément lié à mobx mais que son parent est un observateur mobx-react)

Edit:

Il doit exister un élément lié à l’instance de composant. Des investigations ultérieures ont montré que, dans certains cas, la création d’un gestionnaire dans la fonction de rendu ferait disparaître cet avertissement, mais pas dans tous les cas.

class MyComponent extends React.component {
  constructor (props) {
    // ...
    this.clickMeBound = this.clickMe.bind(this)
  }

  clickMe () {
    ...
  }

  render () {
    // works
    <button onClick={() => {this.clickMe()}}>click arrow in render</button>

    // warning: Can't call setState on a component that is not yet mounted.
    <button onClick={this.clickMeBound}>click bound</button>
  }
}

Edit 2:

J'ai supprimé 'patch-hot-loader/patch' de mes entrées dans ma configuration Webpack et certains problèmes étranges comme celui-ci ont disparu. Je ne mets pas cela comme une réponse car le message d'erreur lui-même est toujours étrange et cela provoque un avertissement dans la console. Tout fonctionne bien cependant.

14
Kev

Comme @ Amida mentionné, hot-loader semble être le problème. Celui qui utilise

app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
    HotModuleReplacement = true,
    ReactHotModuleReplacement = true
});

dans Startup.cs, supprimez-le et le problème disparaîtra. Je ne sais pas pourquoi, mais c'est ma solution actuelle.

EDIT:

La mise à jour de "react-hot-loader" et de "webpack-hot-middleware" avec les dernières versions a résolu le problème

1
Antoan Elenkov

Ajoutez simplement cette ligne de code au code

export default class MyComponent extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
       initial: 'state',
       some: ''          // <-------  this line
    }
   this.clickMe = this.clickMe.bind(this)
  }

 clickMe () {
   this.setState({
     some: 'new state'
   })
  }

 render () {
   return (
     <div>
       <button onClick={this.clickMe}>click</button>
     </div>
   );
  }
}
1
Mahdi Bashirpour

Si cette erreur se produit dans l'un de vos tests, vous devrez peut-être render le composant d'un élément avant d'y accéder (c'est-à-dire simplement faire let app = new App; n'est pas assez). Le rendu va effectivement monter le composant et ses enfants, comme expliqué dans cette autre réponse et vous pourrez ensuite utiliser le résultat pour effectuer des opérations sans déclencher l'erreur setState. Un simple App.test.js Exemple:

import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div); // <-- App component mounted here
  // without JSX: ReactDOM.render(React.createElement(App), div)
  ReactDOM.unmountComponentAtNode(div);
});

test('array length decreased after removal', () => {
  const div = document.createElement('div');
  let app = ReactDOM.render(<App />, div); // <-- App component mounted here
  const origArrLen = app.state.arr.length;

  app.removeAtIndex(0);
  expect(app.state.arr.length).toEqual(origArrLen - 1);
  ReactDOM.unmountComponentAtNode(div);
});

Où le composant App pourrait avoir:

class App extends Component {
  state = {
    arr: [1,2,3]
  };

  removeAtIndex = index => {
    const { arr } = this.state;
    this.setState({ arr: arr.filter((el, i) => i !== index) });
  };
  // render() { return ( ... ) }
}
0
CPHPython