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

时间:2015-12-31 09:39:18

标签: reactjs

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()   零件。这是一个无操作。请检查未定义的代码   成分

我在componentWillUnmount中删除了'EventListener'已结束',但它无效。因为我在this.setState({'time': remainTimeInfo});中添加了componentDidMount

12 个答案:

答案 0 :(得分:96)

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

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

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

答案 1 :(得分:23)

removeEventListeneraddEventListener具有相同的签名。删除侦听器的所有参数必须完全相同。

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

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

并在componentWillUnmount中调用this.cleanup()

答案 2 :(得分:4)

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

示例

更改以下错误代码

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

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

答案 3 :(得分:3)

修改isMounted已弃用,可能会在更高版本的React中删除。请参阅this和此isMounted is an Antipattern

如警告所述,您在 已挂载的组件上调用this.setState但从那时起已卸载。

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

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

确保组件仍然安装。

答案 4 :(得分:3)

因为我更新了最新的反应版本,所以我遇到了同样的问题。解决方法如下。

我的代码是

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

答案 5 :(得分:2)

之前我遇到过这个问题,并根据React官方网页isMounted is an Antipattern解决了这个问题。

isMounted中将属性componentDidMount标记设置为true,并在componentWillUnmount中将其切换为false。当您在回调中setState()时,请先检查isMounted!它对我有用。

state = {
    isMounted: false
  }
  componentDidMount() {
      this.setState({isMounted: true})
  }
  componentWillUnmount(){
      this.setState({isMounted: false})
  }

回调:

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

答案 6 :(得分:1)

这个帖子中的很多答案都得到了使用refs的意义,但我认为一个完整的例子会很好。由于您通过使用事件侦听器并退出React上下文来操作实际DOM节点,因此应将ref视为标准解决方案。这是一个完整的例子:

class someComponent extends Component {
  constructor(props) {
    super(props)
    this.node = null
  }
  render() {
    return (
      <div ref={node => { this.node = node }}>Content</div>
    )
  }
  handleEvent(event) {
    if (this.node) {
      this.setState({...})
    }
  }
  componentDidMount() {
    //as soon as render completes, the node will be registered.
    const handleEvent = this.handleEvent.bind(this)
    this.node.addEventListener('click', handleEvent)
  }
  componentWillUnmount() {
    const handleEvent = this.handleEvent.bind(this)
    this.node.removeEventListener('click', handleEvent)
  }
}

答案 7 :(得分:1)

addEventListener和removeEventListener,Callback不能是Anonymous内部类,并且它们应该具有相同的参数

答案 8 :(得分:0)

当我想在Ajax请求的成功/失败回调中显示弹出窗口(引导模式)时,我收到了此警告。另外setState没有工作,我的弹出模式没有显示。

以下是我的情况 -

<Component /> (Containing my Ajax function)
    <ChildComponent />
        <GrandChildComponent /> (Containing my PopupModal, onSuccess callback)

我从孙子组件调用组件的ajax函数,传递一个onSuccess Callback(在孙子组件中定义),它正在设置状态以显示弹出模式。

我把它改为 -

<Component /> (Containing my Ajax function, PopupModal)
    <ChildComponent />
        <GrandChildComponent /> 

相反,我调用setState(onSuccess Callback)来显示组件(ajax回调)本身的弹出模式并解决问题。

在第二种情况下:组件被渲染两次(我在html中包含了两次bundle.js)。

答案 9 :(得分:0)

audioNode.addEventListener的回调中使用命名方法代替匿名函数应消除主题警告:

    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', this.callbackMethod );
}

callBackMethod = () => {
    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});
    }
}

是的,反正还是需要命名方法,因为removeEventListener不能与匿名回调一起使用,如上所述。

答案 10 :(得分:0)

  1. 取消componentWillUnmount中的所有异步操作
  2. 异步调用setState时,检查组件已经卸载,
    因为isMounted标志已弃用

答案 11 :(得分:0)

我遇到了这个问题,但上述解决方案不起作用。结果我不小心忘记将“this”绑定到我的一个类方法。

那是一个多么漫长的夜晚:(

错误代码

class App extends React.Component{
    constructor(props,context){
        super(props,context)
        this.state={
            x:true,
            y:false
        }
        this.methodY=this.methodY.bind(this)
        this.methodX=this.methodX(this) //error
    }
  }

正确的代码

class App extends React.Component{
    constructor(props,context){
        super(props,context)
        this.state={
            x:true,
            y:false
        }
        this.methodY=this.methodY.bind(this)
        this.methodX=this.methodX.bind(this)

    }
}