Photo by Safar Safarov on Unsplash

This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property <property> on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist()...

If you’ve ever seen the error above and wondered… wait, what? then it is my hope that this article shines some light on what’s really happening and why event.persist() is necessary at all.

Let’s begin by exploring what actually gets passed to our event handlers when an event occurs. The React docs put it very succinctly.

Your event handlers will be passed instances of SyntheticEvent , a cross-browser wrapper around the browser’s native event.

Even though it is a wraps the browser’s native event, SyntheticEvent has the same interface as the native event. This is nice because it means that you get to use the familiar event methods like stopPropagation() and preventDefault() , while enjoying the benefits of working with events in a browser-agnostic way.

Regardless of this, if you find yourself in need of the underlying browser event, you can still access it by reading the nativeEvent attribute on the SyntheticEvent instance that gets passed to your event handler.

This is all well and good but there’s one important detail to keep in mind when working events in React.

Synthetic Events are pooled

For performance reasons, React will prepare the SyntheticEvent instance that gets passed to your event handler, for reuse, by nullifying all properties on the object the event handler has been called. This means that if you try to use the event in an asynchronous way, (say in a set timeout or some other asynchronous code) you won’t get the results you expect.

Let’s drive the point home with an example.

Say we are implementing a username input that checks calls an API endpoint to check that the user’s entry is available. Now say we have the following basic implementation.

While this looks innocent enough, our attempt to read the length of the user’s input on line 12 would cause the code to break, since SyntheticEvent instance would have been nullified by the time execution got to that point.

We can deal with this in a couple of ways:

1) Persist the event

SyntheticEvent instances have a persist() method, that when called, will remove the event from the pool and allow references to the event to be retained by user code. This means we can simply modify our earlier example to include a line (line 19) that explicitly persists the event so that isNameAvailable can still access it even after the event handler ( handleInputChange in our example) has been called. Here’s the modified version:

2) Preserve event attributes

Sometimes you’ll find that you don’t actually need to keep the original event around. In our example, it is obvious that the only reason we needed the original SyntheticEvent instance to stick around, was so that we access its target.value attribute. In these cases, it may be better to assign the attribute(s) to a variable(s) that we can pass around as we please regardless of whether the original event has been nullified. If we modified our example to do this it would look something like this:

This way, it doesn’t matter whether or not the original event has been nullified. We would still be able to access the values that we preserved via assignment. No persist needed!

Conclusion

If you need to access a whole bunch of stuff on the event instance, it may make more sense to keep the event around longer by persisting it, rather than wrangling a whole bunch of variables around. Otherwise extracting the single value you need might be better. It’s neither here nor there. What is important is that evaluate your specific situation and pick the approach — what works for you is good enough.

I truly hope this post has been of some help. Any questions, comments or suggestions for improvement are welcome. Onward!