setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op
ReactjsReactjs Problem Overview
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
.
Reactjs Solutions
Solution 1 - Reactjs
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>
);
};
Solution 2 - Reactjs
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()
.
Solution 3 - Reactjs
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: ''
};
}
Solution 4 - Reactjs
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.
Solution 5 - Reactjs
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} />
});
}
}
Solution 6 - Reactjs
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});}
Solution 7 - Reactjs
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)
}
}
Solution 8 - Reactjs
addEventListener and removeEventListener,the Callback must not be Anonymous inner class,and they should have the same params
Solution 9 - Reactjs
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).
Solution 10 - Reactjs
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.
Solution 11 - Reactjs
- Cancel all async operation in
componentWillUnmount
- Check component is already unmounted when async call
setState
,
since isMounted flag is deprecated