setState(...): 只能更新一个挂载或挂载的组件。这通常意味着您在未安装的组件上调用了 setState()。这是一个无操作

IT技术 reactjs
2021-04-02 23:03:39
componentDidMount(prevProps, prevState, prevContext) {
    let [audioNode, songLen] = [this.refs.audio, List.length-1];

    audioNode.addEventListener('ended', () => {
        this._endedPlay(songLen, () => {
            this._currSong(this.state.songIndex);
            this._Play(audioNode);
        });
    });

    audioNode.addEventListener('timeupdate', () => {
        let [remainTime, remainTimeMin, remainTimeSec, remainTimeInfo] = [];

        if(!isNaN(audioNode.duration)) {
            remainTime = audioNode.duration - audioNode.currentTime;
            remainTimeMin = parseInt(remainTime/60);  // 剩余分
            remainTimeSec = parseInt(remainTime%60);  // 剩余秒

            if(remainTimeSec < 10) {
                remainTimeSec = '0'+remainTimeSec;
            }
            remainTimeInfo = remainTimeMin + ':' + remainTimeSec;
            this.setState({'time': remainTimeInfo});
        }
    });
}

componentWillUnmount () {
    let audio = this.refs.audio;
    audio.removeEventListener('timeupdate');
    audio.removeEventListener('ended');
}

错误:

警告:setState(...):只能更新已安装或正在安装的组件。这通常意味着您在未安装的组件上调用了 setState()。这是一个无操作。请检查未定义组件的代码。

我 removeEventListener 'ended' in componentWillUnmount,但它不起作用。因为我添加this.setState({'time': remainTimeInfo});componentDidMount.

6个回答

我通过为组件分配一个ref然后在设置状态之前检查 ref 是否存在来解决这个问题

myMethod(){
  if (this.refs.myRef) 
   this.setState({myVar: true});
}

render() {
  return (
    <div ref="myRef">
      {this.state.myVar}
    </div>
  );
}

最近,由于我主要使用功能组件,所以我使用了这种模式:

const Component = () => {
  const ref = React.useRef(null);
  const [count, setCount] = React.useState(0);

  const increment = () => {
    setTimeout(() => { // usually fetching API data here
      if (ref.current !== null) {
        setCount((count) => count + 1);
      }
    }, 100);
  };

  return (
    <button onClick={increment} ref={ref}>
      Async Increment {count}
    </button>
  );
};
这也已弃用。但这是一个甜蜜而简单的解决方案!
2021-05-23 23:03:39
为什么不能反应允许设置状态,因为它无关紧要。
2021-05-26 23:03:39
谢谢你!然而,这非常令人恼火。我的几乎所有场景都执行基于 Promise 的获取加载,并抛出此错误。为什么 React 不能默默地拒绝在未挂载状态下运行 setState。这不像任何人会看到差异。
2021-06-13 23:03:39
非常优雅的解决方案!
2021-06-19 23:03:39
神奇的解决方案!!如此简单如此有效!
2021-06-19 23:03:39

removeEventListener与 具有相同的签名addEventListener所有参数必须完全相同才能删除侦听器。

var onEnded = () => {};
audioNode.addEventListener('ended', onEnded, false);

this.cleanup = () => {
  audioNode.removeEventListener('ended', onEnded, false);
}

并在 componentWillUnmount 调用中this.cleanup()

我遇到这个问题是因为我在构造函数中使用了setState而不是state

例子

更改以下错误代码

constructor(props) {
  super(props);
  this.setState({
    key: ''
  });
}

constructor(props) {
  super(props);
  this.state = {
    key: ''
  }; 
}
该死的,我刚才真的是无意中做到了这一点。谢谢
2021-06-18 23:03:39

编辑isMounted已弃用,可能会在更高版本的 React 中删除。看到这个和这个,isMounted 是一个 Antipattern


正如警告所述,您正在调用this.setState一个安装但此后已卸载的组件。

为了确保您的代码是安全的,您可以将其包装在

if (this.isMounted()) {
    this.setState({'time': remainTimeInfo});
}

以确保组件仍处于安装状态。

this.isMounted 已被弃用,它不能解决事件侦听器泄漏的问题(这主要是它被弃用的原因)
2021-05-23 23:03:39
isMounted 是一个反模式 - facebook.github.io/react/blog/2015/12/16/...
2021-05-29 23:03:39
@Kolby,是的,我的回答提到了这一点。
2021-06-13 23:03:39
感谢您指出这一点,但为什么它仍然用于 ie Load Initial Data via AJAX我在哪里可以看到它的当前状态?
2021-06-21 23:03:39

自从我更新了最新的 React 版本以来,我遇到了同样的问题。解决如下。

我的代码是

async componentDidMount() {
  const { default: Component } = await importComponent();
  Nprogress.done();
  this.setState({
    component: <Component {...this.props} />
  });
}

变成

componentWillUnmount() {
  this.mounted = false;
}
async componentDidMount() {
  this.mounted = true;
  const { default: Component } = await importComponent();
  if (this.mounted) {
    this.setState({
      component: <Component {...this.props} />
    });
  }
}