1. 程式人生 > >Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a

原因主要是:

這個錯誤在配合 多次Route的時候會經常出現

Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component. 主要是在執行 Ajax 執行 response 函式時 因為 component 已經 unmounted 導致 setState 執行時所造成的錯誤

與在componentDidMount或componentWillMount中執行的專案中的非同步程式碼(promise,generator或async / await)有關。導致回撥的時候component 已經不復存在

我的錯誤原因在於綁定了click 事件, 所以一定需要解綁,如果是非Route 切換的話, 是不會報錯的。

1. 錯誤的如下

componentDidMount() {

    const {displayLayout} = this.props

    document.addEventListener('click', () => {
        super.defineState('displayLayout', false)
    }, false)

    this.state._input.addEventListener('click', (ev) => {

        super.stop(ev)
        super.defineState(
'displayLayout', true) }, false) }

 

修改成了

1.1. 正確的如下

componentDidMount() {

    const {displayLayout} = this.props
    document.addEventListener('click', this.missLayotu, false)
    this.state._input.addEventListener('click', this.displayLayout, false)
}

componentWillUnmount() {
    document.removeEventListener(
'click',this.missLayotu) this.state._input.removeEventListener('click',this.displayLayout) } missLayotu(){ super.defineState('displayLayout', false) } displayLayout(ev){ super.stop(ev) super.defineState('displayLayout', true) }

 

官方的介紹如下

isMounted是一個反模式

2015年12月16日通過Jim Sproch

隨著我們接近正式棄用isMounted,值得理解為什麼函式是反模式,以及如何在不使用isMounted函式的情況下編寫程式碼。

主要的用例isMounted()setState()在元件解除安裝之後避免呼叫,因為setState()在元件解除安裝後呼叫會發出警告。“setState警告”存在可以幫助你捕捉錯誤,因為呼叫setState()一個解除安裝的元件表明你的應用程式/元件無法正確清理。具體來說,呼叫setState()一個解除安裝的元件意味著你的應用程式在元件被解除安裝後仍然持有對元件的引用 - 這通常表示記憶體洩漏!

為了避免錯誤資訊,人們經常新增這樣的行:

if (this.isMounted()) { // This is bad.
  this.setState({...});
}

 

isMounted呼叫之前的檢查setState()確實消除了警告,但是它也打破了警告的目的,因為現在你永遠不會得到警告(即使你應該!)。

其他用途isMounted()同樣是錯誤的; 使用isMounted()是一種程式碼氣味,因為你會檢查的唯一原因是因為你認為你可能在元件解除安裝後持有一個引用。

任何人升級他們的程式碼以避免一個簡單的遷移策略isMounted()是跟蹤自己安裝的狀態。只需將_isMounted屬性設定為true,componentDidMount並將其設定為false componentWillUnmount,並使用此變數來檢查元件的狀態。

一個最佳的解決方案將是找到setState()一個元件解除安裝後可能被呼叫的地方,並修復它們。這種情況通常是由於回撥,元件在等待某些資料並在資料到達之前取消掛載而發生的。理想情況下,任何回撥應在取消componentWillUnmount之前取消。

例如,如果您在元件中使用Flux商店,則必須退訂componentWillUnmount

class MyComponent extends React.Component {
  componentDidMount() {
    mydatastore.subscribe(this);
  }
  render() {
    ...
  }
  componentWillUnmount() {
    mydatastore.unsubscribe(this);
  }
}

 

如果你使用ES6的承諾,你可能需要包裝你的承諾,以使其可取消。

const cancelablePromise = makeCancelable(
  new Promise(r => component.setState({...}}))
);

cancelablePromise
  .promise
  .then(() => console.log('resolved'))
  .catch((reason) => console.log('isCanceled', reason.isCanceled));

cancelablePromise.cancel(); // Cancel the promise

 

makeCancelable最初由@istarkov定義為:

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then(
      val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
      error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

 

作為提早清理程式碼的附加功能,刪除功能isMounted()使您可以輕鬆升級到isMounted()已禁止使用的ES6類。快樂的編碼!