What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
The “latest ref pattern” keeps changing callback logic available to a long-lived callback without recreating that callback every time its captured values change. For logic invoked from an Effect, React’s current useEffectEvent API is usually the clearer choice. A ref-held callback remains a manual technique for other cases; neither approach should be used to hide values that genuinely need to restart an Effect.
What the latest ref pattern does
A function created during render closes over the props and state from that render. If an interval, subscription, or other long-lived callback keeps using that function after later renders, it may continue seeing old values—a stale closure.
The manual pattern stores the changing callback in a ref, then has a stable long-lived callback read and invoke the current callback from ref.current. The ref persists across renders, and changing its current property does not cause a render. React documents both behaviors in its useRef reference.
This is a low-level technique, not a general-purpose way to make every callback “fresh.” It is useful only when the callback is meant to remain registered or otherwise long-lived while its logic needs to change.
#1 Best Overall
For Effect logic, consider useEffectEvent first
useEffectEvent is React’s purpose-built API for logic called from an Effect that needs the latest committed props or state without making those values restart the Effect. React says that the returned function “always accesses the latest committed values from render at the time of the call.” See the useEffectEvent reference.
For example, a notification triggered by a connection can use the current theme while the connection itself is established only when the room changes:
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected', theme);
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]);
}
Here, roomId determines which connection the Effect synchronizes, so it remains a dependency. A theme change updates the notification logic but does not by itself reconnect the room. If a changed value should cause setup or cleanup to run again, keep it in the Effect dependencies. React explains this distinction in its useEffect guide.
Keep Effect Events inside their intended boundary
An Effect Event is for Effect-related logic, not a replacement for an ordinary event handler or a callback passed to a child. Call it from an Effect or another Effect Event in the same component. Do not call it during render, pass it to child components, or add it to the Effect dependency array. Its identity is intentionally not stable.
Rank #3
Do not use Effect Events to make lint warnings disappear. Dependencies that determine whether an Effect must re-synchronize belong in its dependency list; only logic that should read current values without triggering that re-synchronization belongs in an Effect Event.
When a ref-held callback still makes sense
Outside Effect-local logic—for example, when an API or event system requires a stable callback identity—a manually managed callback ref may fit. The general idea is to update the ref with the latest callback and have the long-lived function invoke ref.current. The exact update mechanism depends on the lifecycle and requirements of the integration; do not assign to ref.current during render as a routine freshness trick.
Rank #4
React advises against reading or writing ref.current during rendering, except for initialization. A ref is mutable storage, not reactive state: if a value affects what the component renders, represent it with state instead. These constraints are described in the React useRef documentation.
Choose based on what should change
| Question | Prefer | Reason |
|---|---|---|
| Is this logic called by an Effect and should read current committed values without restarting synchronization? | useEffectEvent |
It is designed for this Effect-local separation of current logic from synchronization. |
| Does the changed value mean the external system must be set up again? | Effect dependency | The Effect should re-synchronize when a true synchronization input changes. |
| Does an external API require a stable callback outside Effect Event usage? | Possibly a manual callback ref | A ref can hold the current callback without causing a render, but requires careful lifecycle handling. |
| Does the value affect rendered output? | State | Ref mutations do not trigger rendering. |
| Are you trying to make a component accept a ref from its parent? | Component ref API | This is a separate concern from stale callback closures. |
Do not confuse callback refs with React 19 ref-as-prop
React 19 lets function components receive ref as a prop, so new function components that need to expose a ref do not need forwardRef. This changes how a component receives a parent’s ref; it does not update values captured by a callback. React describes forwardRef as planned for deprecation in a future release, but its documentation does not give a removal date. See the React 19 announcement and forwardRef reference. If supporting older React versions, retain the ref approach required by those versions; the cited documentation does not provide a complete cross-version migration recipe.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




