簡體   English   中英

無法在尚未安裝的組件上調用 setState

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

這是我第一次面對這個警告信息。

無法在尚未安裝的組件上調用 setState。

如下:

這是一個無操作,但它可能表明您的應用程序中存在錯誤。 相反,直接分配給this.state或定義一個state = {}; class 屬性在 MyComponent 組件中具有所需的 state。

尚未安裝”部分實際上幾乎沒有意義,因為觸發該問題的唯一方法是通過單擊需要安裝的組件中的按鈕來調用 function 以查看按鈕。 該組件也不會在任何給定時間卸載。

這個虛擬組件在我的應用程序中重現了錯誤:

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 觀察者)

編輯:

一定有一些與組件實例相關的東西,進一步的調查表明,在某些情況下,在渲染 function 中創建一個處理程序會使這個警告消失,但不是在所有情況下。

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:

我已經從我的 Webpack 配置中的條目中刪除了“react-hot-loader/patch”,並且像這樣的一些奇怪問題已經消失。 我沒有將此作為答案,因為錯誤消息本身仍然很奇怪,這會導致控制台中出現警告。 一切正常。

您收到的這個警告是因為您在構造函數中設置了對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 及更高版本,您可以使用帶有“SAFE_”前綴的componentWillMount 方法 [SAFE_componentWillMount]

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

只需將以下行添加到您的代碼中

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

您只需要使用componentDidMount()方法。 正如 React 文檔在組件生命周期中所說,如果您需要從遠程端點加載數據,這是實例化網絡請求的好地方,您可以立即在componentDidMount()調用setState() componentDidMount()

像這樣:

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

您不應在構造函數中使用 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() 方法的調用上。

正如@Amida提到的,熱加載器似乎是問題所在。 誰在使用

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

在 Startup.cs 中,刪除它,問題就會消失。 我不知道為什么,但這是我目前的解決方法。

編輯:

將“react-hot-loader”和“webpack-hot-middleware”更新到最新版本修復了該問題

如果此錯誤發生在您的測試之一中,您可能需要在訪問組件之前將其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 ( ... ) }
}

您應該在constructor() 中分配初始狀態。 然后立即在componentDidMount() 中調用setState () 在瀏覽器更新屏幕之前觸發額外的渲染。 在這種情況下, render()方法將被調用兩次,用戶將看不到中間狀態。

您的代碼將如下所示:

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

export default class MyComponent extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      initial: 'state'  // initialize your state in the constructor()
    }    
  }
  componentDidMount(){
  this.clickMe = this.clickMe.bind(this)
  clickMe () {   // call setState() in componentDidMount()
    this.setState({
      some: 'new state'
    })
  }
}

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

 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM