4

I have a React app with basically a fixed header -- <AppHeader> -- and the rest of the content -- <AppMain>. What I'm trying to do is add a drop shadow to the <AppHeader> when the window is scrolled. So I'm attaching an onScroll handler to a DIV around <AppMain>, and when I detect it's scrolled, I set a state variable scrolled, which gets passed to <AppHeader>, and that component can then deal with adding the appropriate drop shadow class. So my code more or less looks like:

class App extends Component {
  _handleScroll(e) {
    console.log('scrolling:', e.target.scrollTop);
    if (e.target.scrollTop > 0) {
      this.setState({ scrolled: true });
    } else {
      this.setState({ scrolled: false });
    }
  }

  constructor(props) {
    super(props);

    this._handleScroll = this._handleScroll.bind(this);
    this.state = {
      scrolled: false
    };
  }

  render() {
    return (
      <div className="App">
        <AppHeader scrolled={this.state.scrolled} />
        <div className="AppMainScroll" onScroll={this._handleScroll}>
          <AppMain />
        </div>
      </div>
    );
  }
}

The above code works well enough, and my <AppHeader> gets the appropriate prop value when the window is scrolled. However, I notice in my console that the _handleScroll() function keeps getting triggered when the div is scrolled, even if I'm not actively scrolling / interacting with the window. As I'm typing this, my console.log() in _handleScroll() has fired 2000 times, and that window isn't even in the foreground. If I scroll all the way back to the top, _handleScroll() no longer gets triggered.

Am I using onScroll and setState incorrectly here?

6
  • Have you tried to debounce the scroll handler? Commented Aug 16, 2017 at 19:11
  • I'm not sure why this is, but you could try attaching your handler to window directly in componentDidMount (remember to remove the handler in componentWillUnmount). This way you also don't need a wrapper. Commented Aug 16, 2017 at 19:12
  • Is the drop shadow constant? For example, like stack overflows header dropshadow(adds a slight shadow when document top is not 0)? Or does the drop shadow only appear when your literally scrolling. Commented Aug 16, 2017 at 19:45
  • minor comment but you can replace your if statement with: this.setState({ scrolled: e.target.scrollTop > 0 }) Commented Aug 16, 2017 at 20:10
  • @DannyDelott I just tried adding lodash's debounce as detailed here, but now the event is firing every 500ms (my debounce timeout), instead of immediately. Commented Aug 16, 2017 at 21:18

1 Answer 1

1

I think this is what you're missing.

componentDidMount() {
  window.onscroll = () => this._handleScroll()
}

_handleScroll() {
  console.log('scrolling:', document.documentElement.scrollTop);
  if (document.documentElement.scrollTop > 0) {
    this.setState({ scrolled: true });
  } else {
    this.setState({ scrolled: false });
  }
}

https://codesandbox.io/s/nk6o110464

Sign up to request clarification or add additional context in comments.

1 Comment

I just went back to the codesandbox.io link and it didn't work like it did before. After some playing around a bit I found that changing document.body.scrollTop to document.documentElement.scrollTop got it working again. The original code showed 0 and wouldn't change when I scrolled. I've updated the answer and the codesandbox.io.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.