I'm hoping someone can shed some light on how React's state and event handlers interact with an event handler set directly on the window object itself(outside of the react lifecycle). For example, in the following code sample we set an event handler directly on the window
object. Now my understanding of how the browser is handling window events is that when the event fires the browser is placing the callback function into the event loop's queue.
In this case, where the handler is set directly on the window, the enqueuing of the event handler function into the event loop is happening outside/independent of the React component lifecycle. However, the event handler calls React's setState
which asynchronously updates the component state. The updating happens asynchronously so that React can more efficiently batch the state changes together so as to avoid re-rendering too often.
import { Component, createRef } from "react";
class GridView extends Component {
constructor(props) {
super(props);
this.state = {
hasScrolled: false
}
this.handleScrollEvent = this.handleScrollEvent.bind(this);
}
componentDidMount() {
window.addEventListener("scroll", this.handleScrollEvent);
}
componentWillUnmount() {
window.removeEventListener("scroll", this.handleScrollEvent);
}
handleScrollEvent() {
this.setState({hasScrolled: true})
}
render() {
return <div > ... some scrollable content </div>
}
}
My question is this: Can I be certain that when the handleScrollEvent
callback is fired for the first time, after the page initially scrolls, that the component's hasScrolled
state value will update to true
before the next firing of handleScrollEvent
?
My understanding is that the asynchronous nature of React's setState
makes it so that I cannot be 100% certain of when the state will be updated in relation to an event being triggered that is bound to the window
. Am I understanding this correctly?
To go even further, let's say I was to move the scroll event handler into the React component lifecycle by adding an onScroll
callback inside the component's render
method like below. My assumption is that even though the scroll event handler and the state update are both managed within React that I still can't be sure of the exact ordering of callback and state changes because event handlers in React are asynchronous and batched just like setState
.
import { Component, createRef } from "react";
class GridView extends Component {
constructor(props) {
super(props);
this.state = {
hasScrolled: false
}
this.handleScrollEvent = this.handleScrollEvent.bind(this);
}
handleScrollEvent() {
this.setState({hasScrolled: true})
}
render() {
return <div onScroll={ this.handleScrollEvent } > ... some scrollable content </div>
}
}
So, can you ever really be sure of the ordering of state updates in relation to event handlers in React? If the answer is no, you cannot ever be 100% sure, then should logic inside of event handlers rely on a component's instance variables rather than a component's state given that instance variables are updated synchronously?