1
votes

When I leave a route component, I get the following warning:

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.

The warning started appearing after I added the scroll event listener. How do I prevent this warning? I'm binding the event method only once. The event listener is added in componentDidMount and removed in componentWillUnmount.

class DashboardRoute extends React.Component {
    constructor (props) {
        super(props)
        this.handleScroll = this.handleScroll.bind(this);
        this.state = {
            scrolled: false
        };
    }

    componentDidMount () {
        window.addEventListener('scroll', throttle(this.handleScroll, 250));
    }

    componentWillUnmount () {
        window.removeEventListener('scroll', throttle(this.handleScroll, 250));
    }

    handleScroll (e) {
        let scrolled = (window.scrollY > 0);
        if (scrolled !== this.state.scrolled) {
            this.setState({scrolled: scrolled});
        }
    }

    render () {
        return (
            <div>
                <!-- Component code -->
            </div>
        );
    }
}
1
What is unclear about the warning? Which part do you not understand?Felix Kling

1 Answers

0
votes

Since the scroll event handler can be delayed as much as 250 ms, your component might be unmounted before it is called. This depends on how DashboardRoute is used throughout your app.

You can use an instance variable called e.g. _mounted that keep track of if the component is actually still mounted or not and use that in your scroll handler before you use setState.

window.removeEventListener('scroll', throttle(this.handleScroll, 250)) will also try to remove a newly created function as scroll listener, so the listener created in window.addEventListener('scroll', throttle(this.handleScroll, 250)) will not be removed. You can create the throttled method once instead and reference that.

Example

class DashboardRoute extends React.Component {
  _mounted = true;
  state = {
    scrolled: false
  };

  componentDidMount() {
    window.addEventListener("scroll", this.handleScroll);
  }

  componentWillUnmount() {
    this._mounted = false;
    window.removeEventListener("scroll", this.handleScroll);
  }

  handleScroll = throttle(e => {
    let scrolled = window.scrollY > 0;
    if (this._mounted && scrolled !== this.state.scrolled) {
      this.setState({ scrolled: scrolled });
    }
  }, 250);

  render() {
    return <div>{/* ... */}</div>;
  }
}