trololol
trololol

Reputation: 19

React component unmounted

My react app as routes like so:

<Route handler={RouteHandler}>
    <Route name="welcome" path="welcome" handler={WelcomePage} />
    <Route name="app" path="/" handler={Application}>
        <Route name="some-page" path="some-page" handler={SomePage} />
    </Route>
</Route>

The main "App" layout is the following

export default class Application extends React.Component {
    render() {
        return (<div>
            <ModalView />
            <TopBar />
            <RouteHandler />
        </div>);
    }
}

The TopBar that is giving me problems:

export default class TopBar extends React.Component {
    componentDidMount() {
        userStore.addChangeListener(this._onChange);
    }
    componentWillUnmount() {
        userStore.removeChangeListener(this._onChange);
    }
    _onChange = () => {
        this.setState(this.getState());
    };
    handleLoginClick() {
        actions.queueModal("login");
    }
    handleSignupClick() {
        actions.queueModal("signup");
    }
    getState() {
        return {
            currentUser: userStore.currentUser
        };
    }
    state = this.getState();
    render() {
        return (
            <div className="topBar">
                {this.state.currentUser ?
                    (<Link to="home"><button className="default">{this.state.currentUser.email}</button></Link>) :
                    ([
                        <button key={1} className="clear" onClick={this.handleSignupClick}>Sign up</button>,
                        <button key={2} className="clear" onClick={this.handleLoginClick}>Log in</button>
                    ])}
            </div>
        );
    }
}

According to the "App" layout, the TopBar should be mounted when I am in some-page. Now when I complete login, the userStore emits a change, which is received by the TopBar. Instead of the bar updating itself, I get an error message like "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." Why is this?

Upvotes: 1

Views: 2188

Answers (2)

trololol
trololol

Reputation: 19

It turns out... The component that was "unmounted" was not even the TopBar. It was a modal that I had open like so:

export default class ModalView extends React.Component {
    componentDidMount() {
        notificationStore.addChangeListener(this._onChange);
    }
    componentWillUnmount() {
        notificationStore.removeChangeListener(this._onChange);
    }
    _onChange = () => {
        this.setState(this.getState());
    };
    getState() {
        return {
            modal: notificationStore.getModal(),
            test: 123
        };
    }
    state = this.getState();
    render() {
        if (!this.state.modal) {
            return <noscript />;
        }
        else {
            return (<div>
                <div className="modalBackground">
                    <LoginModal />
                    }
                </div>
            </div>);
        }
    }
}

This unmounted the loginModal, which was listening to userStore updates as well, hence the "trying to update unmounted component".

Moral of the story is to always name components so that error messages are more specific: Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. Please check the code for the ExampleApplication component. This is a no-op.

Upvotes: 0

trquoccuong
trquoccuong

Reputation: 2873

It look like you dont have the initial State for component TopBar. Try to set Intial state in contructor .

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

Upvotes: 1

Related Questions