Avoid re-rendering on scroll and increase performance in React web app
I'm having trouble working with React, basically I have 2 compact menus that pop up when the page is scrolled to x distance from the top. As I can see, other components of the web application take small steps when scrolling, the reason is they are being redrawn.
To find out the distance to the top while the user is scrolling, I have an eventlistener on componentDidMount () and I think this is causing the problem, but I'm not sure as you know the stable version is pretty new and I also new to this technology:
Here is some code, exactly the top bar that contains the nested components, and triggers the compact menu bar when the user scans over 100 pixels from the top:
export default class AppTopBar extends Component {
constructor (props){
super(props);
this._bind("_handleOnScrollDocument");
this.state = {
StatusBar: false
}
}
componentDidMount() {
if (process.env.BROWSER) {
document.addEventListener("scroll", (e) => this._handleOnScrollDocument(e), true);
}
}
_handleOnScrollDocument(e) {
if (e.target.body.scrollTop > 100) {
this.setState({ StatusBar: true });
} else {
this.setState({ StatusBar: false });
}
}
render() {
return (
<div className="aui-core-apptopbar">
<StatusBar companies={this.props.companies} compactMenuItems={this.props.compactMenuItems} StatusBar={this.state.showCompactStatusBar}/>
<StatusBar companies={this.props.companies} userOptions={this.props.userOptions} compactMenuItems={this.props.compactMenuItems}/>
<MainNavBarView menuItems={this.props.menuItems}/>
</div>
);
}
}
I have researched and read information about component lifecycle and other features like: PURERENDERMIXIN
Do you know it's better to do something to get lightweight web apps and avoid re-rendering on scroll?
source to share
Let's say you create a local variable flag
for your class. And then:
_handleOnScrollDocument(e) {
let newFlag = (e.target.body.scrollTop > 100);
if (flag !== newFlag) {
this.setState({showCompactStatusBar: newFlag});
flag = newFlag;
}
};
This will only change the state (and re-render) when the scrollTop (100) threshold is passed in one direction or another. Otherwise, we don't need to set the state at all while avoiding render
.
source to share
The "built-in" solution is to use a method shouldComponentUpdate
that allows you to compare the current props
and state
component to the input props
and state
then, if you choose:
_handleOnScrollDocument(e) {
// set the state no matter what
this.setState({
showCompactStatusBar: e.target.body.scrollTop > 100
});
},
shouldComponentUpdate(newProps, newState) {
// only render if the state has changed
return this.state.showCompactStatusBar !== newState.showCompactStatusBar;
}
source to share
I had a scroll event on a component inside my page where the overflow: auto
CSS rule was . I hooked up a scroll event listener to this component as it was targeted for in this blog post . However, setting the last scroll position to a state can severely hinder performance.
So instead, I defined the last position inside the class constructor and changed it there, and avoided re-rendering on every scroll.
constructor(props){
super(props)
this.state = { scrollDirection: 'up' }
this.lastY = 0
}
I would only change state if direction changed.
handleListScroll(event){
const Y = event.currentTarget.scrollTop
const lastY = this.lastY
if (Y > lastY) {
scrollDirection = 'down'
if (this.state.scrollDirection === 'up') {
this.setState({
scrollDirection,
})
}
}
if (Y < lastY) {
scrollDirection = 'up'
if (this.state.scrollDirection === 'down') {
this.setState({
scrollDirection
})
}
}
this.lastY = Y
}
source to share