Javascript 无法在尚未安装的组件上调用 setState

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/50162522/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:33:57  来源:igfitidea点击:

Can't call setState on a component that is not yet mounted

javascriptreactjsmobxmobx-react

提问by Kev

this is the first time I face this warning message.

这是我第一次面对这个警告信息。

Can't call setState on a component that is not yet mounted.

无法在尚未安装的组件上调用 setState。

Follows:

如下:

This is a no-op, but it might indicate a bug in your application. Instead, assign to this.statedirectly or define a state = {};class property with the desired state in the MyComponent component.

这是一个空操作,但它可能表明您的应用程序中存在错误。相反,在 MyComponent 组件中this.state直接分配或定义state = {};具有所需状态的类属性。

The "not yet mounted" part actually makes little to no sense as the only way to trigger the issue is to call a function by clicking a button from a component that needs to be mounted in order to see the button. The component is not unmounted at any given time neither.

尚未安装”部分实际上几乎没有任何意义,因为触发该问题的唯一方法是通过单击需要安装的组件中的按钮来调用函数以查看该按钮。该组件也不会在任何给定时间卸载。

This dummy component reproduces the error in my app:

这个虚拟组件重现了我的应用程序中的错误:

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

I am using:

我在用:

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

Did I miss something in the latest React/mobx version? (note the component does not use any mobx related stuff but its parent is a mobx-react observer)

我错过了最新的 React/mobx 版本吗?(注意该组件不使用任何与 mobx 相关的东西,但它的父级是一个 mobx-react 观察者)

Edit:

编辑:

There must be something related to the component instance, further investigation has shown that in some cases, creating an handler inside the render function will make this warning disappear, but not in all cases.

肯定有与组件实例相关的东西,进一步调查表明,在某些情况下,在渲染函数中创建处理程序会使此警告消失,但并非在所有情况下。

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:

编辑2:

I have removed 'react-hot-loader/patch' from my entries in my Webpack config and some weird issues like this one have disappeared. I'm not putting this as an answer because the error message itself is still weird and this causes a warning in the console. Everything works fine though.

我已经从我的 Webpack 配置中的条目中删除了“react-hot-loader/patch”,一些像这样的奇怪问题已经消失了。我没有把它作为答案,因为错误消息本身仍然很奇怪,这会导致控制台中出现警告。不过一切正常。

回答by Bashirpour

Just add following line to your 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>
   );
  }
}

回答by Anuj Shukla

This warning that you are getting is because you are setting a reference to clickMe methodin the constructor, which is then using the setState().

您收到的这个警告是因为您在构造函数中设置了对clickMe 方法的引用,然后使用setState()

constructor (props) {
    super(props)
    this.state = {
       initial: 'state',
       some: ''          
    }
   this.clickMe = this.clickMe.bind(this);   <--- This method
  }

 clickMe () {
   this.setState({
     some: 'new state'    <-- the setState reference that is causing the issue
   })
  }

Try removing the this.clickMe = this.clickMe.bind(this)from constructor and do it in a lifecycle method like componentWillMount() or ComponentDidMount(). For react 16 and above you can use the componentWillMount methodwith "SAFE_"prefix. [SAFE_componentWillMount]

尝试从构造函数中删除this.clickMe = this.clickMe.bind(this)并在生命周期方法中执行此操作,例如componentWillMount() 或 ComponentDidMount()。对于 react 16 及更高版本,您可以使用带有“SAFE_”前缀的componentWillMount 方法[SAFE_componentWillMount]

 componentWillMount() {
      this.clickMe = this.clickMe.bind(this);
 }

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

回答by Antoan Elenkov

As @Amidamentioned, hot-loader seems to be the issue. Whoever is using

正如@Amida提到的,热加载器似乎是问题所在。谁在使用

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

in Startup.cs, remove it and the issue will disappear. I don't know why, but this is my current workaround.

在 Startup.cs 中,删除它,问题就会消失。我不知道为什么,但这是我目前的解决方法。

EDIT:

编辑:

Update of "react-hot-loader" and "webpack-hot-middleware" to latest versions fixed the issue

将“react-hot-loader”和“webpack-hot-middleware”更新到最新版本修复了该问题

回答by CPHPython

If this error is happening in one of your tests, you might need to renderthe component to an element before accessing it (i.e. simply doing let app = new App;is not enough). Rendering will effectively mountthe component and its children, as explained in this other answerand then you will be able to use the result object to perform operations without triggering the setStateerror. A simple App.test.jsexample:

如果在您的测试之一中发生此错误,您可能需要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);
});

Where the Appcomponent could have:

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

回答by Dagim Belayneh

You just need to use the componentDidMount()method. As the React documentation tells in the component life cycle, if you need to load data from a remote endpoint, this is a good place to instantiate the network request and you may call setState()immediately in componentDidMount().

您只需要使用该componentDidMount()方法。正如 React 文档在组件生命周期中所说,如果您需要从远程端点加载数据,这是实例化网络请求的好地方,您可以setState()立即调用componentDidMount().

Like this:

像这样:

componentDidMount(){
  this.clickMe = this.clickMe.bind(this);
}
clickMe () {
  this.setState({
    some: 'new state'
  })
}