Update style of a component onScroll in React.js
You should bind the listener in componentDidMount
, that way it's only created once. You should be able to store the style in state, the listener was probably the cause of performance issues.
Something like this:
componentDidMount: function() {
window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount: function() {
window.removeEventListener('scroll', this.handleScroll);
},
handleScroll: function(event) {
let scrollTop = event.srcElement.body.scrollTop,
itemTranslate = Math.min(0, scrollTop/3 - 60);
this.setState({
transform: itemTranslate
});
},
with hooks:
import React, { useEffect, useState } from 'react';
function MyApp () {
const [offset, setOffset] = useState(0);
useEffect(() => {
const onScroll = () => setOffset(window.pageYOffset);
// clean up code
window.removeEventListener('scroll', onScroll);
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
console.log(offset);
};
You can pass a function to the onScroll
event on the React element: https://facebook.github.io/react/docs/events.html#ui-events
<ScrollableComponent
onScroll={this.handleScroll}
/>
Another answer that is similar: https://stackoverflow.com/a/36207913/1255973
My solution for making a responsive navbar ( position: 'relative' when not scrolling and fixed when scrolling and not at the top of the page)
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll(event) {
if (window.scrollY === 0 && this.state.scrolling === true) {
this.setState({scrolling: false});
}
else if (window.scrollY !== 0 && this.state.scrolling !== true) {
this.setState({scrolling: true});
}
}
<Navbar
style={{color: '#06DCD6', borderWidth: 0, position: this.state.scrolling ? 'fixed' : 'relative', top: 0, width: '100vw', zIndex: 1}}
>
No performance issues for me.
to help out anyone here who noticed the laggy behavior / performance issues when using Austins answer, and wants an example using the refs mentioned in the comments, here is an example I was using for toggling a class for a scroll up / down icon:
In the render method:
<i ref={(ref) => this.scrollIcon = ref} className="fa fa-2x fa-chevron-down"></i>
In the handler method:
if (this.scrollIcon !== null) {
if(($(document).scrollTop() + $(window).height() / 2) > ($('body').height() / 2)){
$(this.scrollIcon).attr('class', 'fa fa-2x fa-chevron-up');
}else{
$(this.scrollIcon).attr('class', 'fa fa-2x fa-chevron-down');
}
}
And add / remove your handlers the same way as Austin mentioned:
componentDidMount(){
window.addEventListener('scroll', this.handleScroll);
},
componentWillUnmount(){
window.removeEventListener('scroll', this.handleScroll);
},
docs on the refs.