In React, when handling events like onChange
or onClick
, the SyntheticEvent
object is pooled for performance reasons—meaning it gets nullified after the event handler finishes. If you try to access event.target.value
asynchronously (e.g., inside a setTimeout
, Promise
, or async/await
), you’ll get an error like:
Cannot read property 'value' of null
Solutions:
1. Store event.target.value
synchronously
Extract the value before the async operation:
const handleChange = async (event) => {
const value = event.target.value; // ✅ Store synchronously
await fetchData(value); // Safe to use later
};
2. Use event.persist()
(Legacy React ≤16)
For older React versions, you could explicitly keep the event:
const handleChange = (event) => {
event.persist(); // ⚠️ Prevents nullification (not needed in React 17+)
setTimeout(() => {
console.log(event.target.value); // Works
}, 1000);
};
(Note: event.persist()
is unnecessary in React 17+ since event pooling was removed.)
3. Use a ref (if dealing with forms)
If you need the latest value after an async call, consider using a ref:
const inputRef = useRef();
const handleChange = async () => {
const value = inputRef.current.value; // Always up-to-date
await fetchData(value);
};
return <input ref={inputRef} onChange={handleChange} />;
4. Use useState
for controlled components
For forms, it’s better to manage state reactively:
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value); // Store in state
};
const handleSubmit = async () => {
await fetchData(inputValue); // Uses latest state
};
return <input value={inputValue} onChange={handleChange} />;
Why Does This Happen?
- React ≤16: Used event pooling (reusing event objects for performance).
- React 17+: No longer pools events, but async operations can still lead to race conditions if the DOM changes.
Best Practice
✅ For forms: Use controlled components (useState
+ onChange
).
✅ For async ops: Extract values synchronously before await
.
✅ For refs: Use useRef
if you need direct DOM access.