Reputation: 5054
I am getting the following warning
"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 ContactPage component."
When I initially go to the contact page the firs time it is fine. Then if I navigate off the page and go back the warning is thrown.
Contact page component :
import React, { Component, PropTypes } from 'react';
import AppStore from '../../stores/AppStore';
import AppActions from '../../actions/AppActions';
import DataContent from './DataContent';
const title = 'Contact Us';
class ContactPage extends Component {
constructor(props) {
super(props);
this.state = AppStore.getState();
AppActions.getData();
}
static contextTypes = {
onSetTitle: PropTypes.func.isRequired,
};
componentWillMount() {
this.context.onSetTitle(title);
AppStore.listen(this.onChange.bind(this));
}
componentWillUnmount() {
AppStore.unlisten(this.onChange.bind(this));
}
onChange(state) {
this.setState(state);
}
renderData() {
return this.state.data.map((data) => {
return (
<DataContent key={data.id} data={data} />
)
})
}
render() {
return (
<div className={s.root}>
<div className={s.container}>
<h1>{title}</h1>
<div>
{ this.renderData() }
</div>
</div>
</div>
);
}
}
export default ContactPage;
When I put debuggers in, on load of contact page it hits componentWillMount(). When I leave the contact page it hits componentWillUnmount(). When I navigate back to the page it hits componentWillMount() again and then throws the error when it hits the onChange(state) function.
Upvotes: 22
Views: 34734
Reputation: 1
I was struggling with this issue as well for quite a while.
What solved for me was to do as @Felix Kling proposed but also make sure my callback function was declared within the component's class, otherwise you're not able to use the this
prefix, wich was apparently what did the trick for me.
Here is an example of what I mean:
Legal, but doesn't work
function onChange() {
this.setState({ MyState: newState })
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
onChange = onChange.bind(this);
}
componentDidMount() {
window.addEventListener('resize', onChange);
}
componentWillUnmount() {
window.removeEventListener('resize', onChange);
}
render() { ... }
}
Works
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange() {
this.setState({ MyState: newState })
}
componentDidMount() {
window.addEventListener('resize', this.onChange);
}
componentWillUnmount() {
window.removeEventListener('resize', this.onChange);
}
render() { ... }
}
Hope it helps anyone else facing this problem. :)
Upvotes: -1
Reputation: 79
In your case add a ref to your root div and check the ref value before you call setState.
Your onChange
method should looks like this:
onChange(state) {
this.refs.yourRef ? this.setState(state) : null;
}
Upvotes: 0
Reputation: 5107
Before the change, state check component is mounted.
render() {
return (
<div ref="root" className={s.root}>
// ----
</div>
)};
onChange(state) {
if(this.refs.root) {
this.setState(state);
}
}
I think this will help to you.
Upvotes: 6
Reputation: 816462
The issue is that the listener of the previous component instance is still registered. And because the previous instance isn't mounted anymore, you get that error.
.bind
always returns a new function. So if you do
AppStore.unlisten(this.onChange.bind(this));
then you are trying to remove a listener that doesn't exist (which fails of course). It does not remove the listener you registered with AppStore.listen(this.onChange.bind(this))
To solve this, you should bind the handler once in the constructor:
this.onChange = this.onChange.bind(this);
and then use AppStore.listen(this.onChange)
and AppStore.unlisten(this.onChange)
.
Upvotes: 38