无法在尚未挂载的组件上调用setState

时间:2018-05-03 19:25:10

标签: javascript reactjs mobx mobx-react

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

  

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

如下:

  

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

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

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

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-react观察者)

修改

必须有与组件实例相关的内容,进一步的调查表明,在某些情况下,在render函数中创建一个处理程序会使此警告消失,但并非在所有情况下都消失。

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:

我已经删除了&#39; react-hot-loader / patch&#39;从我在Webpack配置中的条目和一些像这样的奇怪问题已经消失。我没有把它作为答案,因为错误消息本身仍然很奇怪,这会在控制台中引发警告。一切都很好。

7 个答案:

答案 0 :(得分:5)

收到此警告是因为您在构造函数中设置了对 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
   })
  }

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

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

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

答案 1 :(得分:3)

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

赞:

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

答案 2 :(得分:1)

您的示例没有重现错误。(轻微修改) codesandbox

答案 3 :(得分:1)

只需将这行代码添加到代码

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

答案 4 :(得分:1)

@Amida所述,热加载器似乎是问题所在。谁在使用

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

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

编辑:

将“ react-hot-loader”和“ webpack-hot-middleware”更新为最新版本

答案 5 :(得分:1)

您不应在构造函数中使用setState ,因为尚未安装组件。在这种情况下,clickMe()方法将调用setState()。
而是直接初始化状态。喜欢,

constructor(props) {
    super(props);
    // Don't call this.setState() here!
    this.state = { counter: 0 };
    this.handleClick = this.handleClick.bind(this);
}

如果来自https://reactjs.org/docs/react-component.html#constructor

使用setState(),以便我们可以通过重新渲染来反映状态更改。由于尚未渲染组件,因此我们可以直接更改状态,更改将反映在render()方法的调用上。

答案 6 :(得分:0)

如果在您的一项测试中发生此错误,则可能需要在访问元素之前将组件render let app = new App;(即仅做setState是不够的)。渲染将有效地mount the component and its children, as explained in this other answer,然后您将能够使用结果对象执行操作而不会触发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 ( ... ) } } 组件可能具有的位置:

sklearn.preprocessing.LabelBinarizer()