[reactjs] setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op

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

Error:

Warning: setState(...): 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.

I removeEventListener 'ended' in componentWillUnmount, but it is not working. because I add this.setState({'time': remainTimeInfo}); in componentDidMount.

This question is related to reactjs

The answer is


  1. Cancel all async operation in componentWillUnmount
  2. Check component is already unmounted when async call setState,
    since isMounted flag is deprecated

I solved this by assigning a ref to the component and then checking if the ref exists before setting the state:

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

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

And lately, since I am using mostly functional components, I am using this pattern:

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

Edit: isMounted is deprecated and will probably be removed in later versions of React. See this and this, isMounted is an Antipattern.


As the warning states, you are calling this.setState on an a component that was mounted but since then has been unmounted.

To make sure your code is safe, you can wrap it in

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

to ensure that the component is still mounted.


I had this problem before, and solved it according to React official page isMounted is an Antipattern.

Set a property isMounted flag to be true in componentDidMount , and toggle it false in componentWillUnmount. When you setState() in your callbacks, check isMounted first! It works for me.

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

callback:

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

addEventListener and removeEventListener,the Callback must not be Anonymous inner class,and they should have the same params


I have faced same problem since I have updated the latest react version. Solved like below.

My code was

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

Changed to

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

removeEventListener has the same signature as addEventListener. All of the arguments must be exactly the same for it to remove the listener.

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

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

And in componentWillUnmount call this.cleanup().


Having named method in place of the anonymous function in audioNode.addEventListener 's callback should eliminate the subject warning:

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

And yes, named method is needed anyways because removeEventListener won't work with anonymous callbacks, as mentioned above several times.


I encountered this issue because I used setState instead of state in the constructor.

EXAMPLE

Change the following incorrect code

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

to

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

A lot of the answers in this thread get the point of using refs, but I think a complete example would be good. Since you're operating on an actual DOM node by using the event listener and stepping out of the React context, a ref should be considered the standard solution. Here's a complete example:

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

I was getting this warning when I wanted to show a popup (bootstrap modal) on success/failure callback of Ajax request. Additionally setState was not working and my popup modal was not being shown.

Below was my situation-

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

I was calling ajax function of component from grandchild component passing a onSuccess Callback (defined in grandchild component) which was setting state to show popup modal.

I changed it to -

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

Instead I called setState (onSuccess Callback) to show popup modal in component (ajax callback) itself and problem solved.

In 2nd case: component was being rendered twice (I had included bundle.js two times in html).