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
.
Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op. Warning: Can't call setState (or forceUpdate) on an unmounted component.
Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
Summary. Seeing called setState() on an unmounted component in your browser console means the callback for an async operation is still running after a component's removed from the DOM. This points to a memory leak caused by doing redundant work which the user will never benefit from.
You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won't see the intermediate state.
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> ); };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With