Не удается вызвать setState для компонента, который еще не установлен

это первый раз, когда я сталкиваюсь с этим предупреждением.

Невозможно вызвать setState для компонента, который еще не смонтирован.

Следит:

Это не работает, но это может указывать на ошибку в вашем приложении. Вместо этого присвойте this.state напрямую или определите state = {}; свойство класса с желаемым состоянием в компоненте MyComponent.

" Еще не смонтированная " часть фактически не имеет особого смысла, поскольку единственный способ вызвать проблему - вызвать функцию, нажав кнопку в компоненте, который необходимо смонтировать, чтобы увидеть кнопку. Компонент не демонтируется в любой момент времени.

Этот фиктивный компонент воспроизводит ошибку в моем приложении:

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

Я использую:

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

Я что-то упустил в последней версии React/mobx? (обратите внимание, что компонент не использует ничего, связанного с MOBX, но его родитель является наблюдателем MOBX).

Редактировать:

Должно быть что-то, связанное с экземпляром компонента, дальнейшее исследование показало, что в некоторых случаях создание обработчика внутри функции рендеринга делает это предупреждение исчезающим, но не во всех случаях.

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

Изменить 2:

Я удалил "response-hot-loader/patch" из своих записей в конфигурации Webpack, и некоторые странные проблемы, подобные этой, исчезли. Я не помещаю это как ответ, потому что само сообщение об ошибке все еще странно, и это вызывает предупреждение в консоли. Все работает хорошо, хотя.

Ответ 1

Ваш пример не воспроизводит ошибку. (Slight Modifications) codesandbox

Ответ 2

Просто добавьте эту строку кода в код

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

Ответ 3

Как упомянул @Amida, похоже, проблема в горячем загрузчике. Кто бы ни использовал

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

в Startup.cs удалите его, и проблема исчезнет. Я не знаю почему, но это мой текущий обходной путь.

РЕДАКТИРОВАТЬ:

Обновление "response-hot-loader" и "webpack-hot-middleware" до последних версий устранило проблему

Ответ 4

Если эта ошибка возникает в одном из ваших тестов, вам может потребоваться render компонент для элемента, прежде чем получить к нему доступ (то есть простого выполнения let app = new App; недостаточно). При рендеринге будет эффективно монтироваться компонент и его дочерние элементы, как описано в этом другом ответе, и тогда вы сможете использовать объект результата для выполнения операций, не вызывая ошибку setState. Простой пример App.test.js:

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

Где может быть компонент App:

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