React SyntheticEvent reuse
React implements its own event system in order to provide cross-browser compatibility support. Basically, React wraps the browser native event into an instance of the React SyntheticEvent
and passes it in React event handlers. SyntheticEvent
has the same interface as a native event which means that you can use methods like preventDefault()
and stopPropagation()
.
How does React manage the event system?
For performance reasons, React reuses the SyntheticEvent
objects by pooling them. This means that the SyntheticEvent
object is reused, so after an event handler is invoked all the properties on event.target
are nullified.
“Warning: This synthetic event is reused for performance reasons”
This warning is triggered when we try to access to a React synthetic event in an asynchronous way. Because of the reuse, after the event callback has been invoked the synthetic event object will no longer exist so we cannot access its properties.
The most common example of an asynchronous access to a SyntheticEvent
is within a setState
. When we update a component state through the updater function, the updater will be called asynchronously and the event will be already nullified.
Solution 1: event.persist()
Calling event.persist()
on the synthetic event removes the event from the pool allowing references to the event to be retained asynchronously.
Solution 2: cache the needed properties
We can store the event properties in the event handler and pass them to the asynchronous callback instead of accessing the event directly from the asynchronous callback:
Debouncing a synthetic event handler.
Another common example apart from setState
is a debounced callback:
The debounce
method from lodash creates the function which will be invoked asynchronously so if we try to access the object directly from the debounced callback, the synthetic event will be already nullified.
The example below fixes the problem by caching the event properties. In this case, we access the event value out of the debounced code and pass it to the debounced callback.
Summing up
React wraps native browser events into instances of the SyntheticEvent
. A synthetic event cannot be accessed asynchronously because React reuses it once its handler is called. As possible solutions, we can call event.persist()
or we can cache the needed event properties until they’re finally used.