bun/react-no-latest-value-ref
Disallow a ref that is set to the latest value on every render
A common trick keeps the latest props or state in a ref, so that an effect can read them without listing them as dependencies. Setting ref.current while the component renders isn't safe. React can render a component more than once, or throw the result away, so the ref can hold a value from a render that never reached the screen.
An effect that only copies the value into the ref is safe, but it's reported too. A function from useEffectEvent() sees the latest values without a ref.
Configure#
Off by default. To turn it on:
[lint.rules]
"bun/react-no-latest-value-ref" = "error" # or "warn"Examples#
import { useRef } from "react";
function Chat({ onMessage }) {
const onMessageRef = useRef(onMessage);
onMessageRef.current = onMessageonMessageRef.current is set during render. React may render more than once, or throw the result away.;
// ...
}import { useEffect, useRef } from "react";
function Chat({ onMessage }) {
const onMessageRef = useRef(onMessage);
useEffect(() => {
onMessageRef.current = onMessageThis effect only copies a value into onMessageRef.current. Use useEffectEvent() to read the latest values without a ref.;
});
// ...
}import { useEffect, useEffectEvent } from "react";
function Chat({ room, onMessage }) {
const handleMessage = useEffectEvent(message => onMessage(message));
useEffect(() => {
const connection = connect(room);
connection.on("message", message => handleMessage(message));
return () => connection.close();
}, [room]);
// ...
}A ref that's set under a condition, in an event handler, or in an effect that also does something else isn't reported:
import { useEffect, useRef } from "react";
function Player({ src }) {
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = createPlayer();
}
const startedAt = useRef(0);
useEffect(() => {
startedAt.current = Date.now();
playerRef.current.play(src);
}, [src]);
return <button onClick={() => (startedAt.current = 0)}>Reset</button>;
}When not to use it#
useEffectEvent() needs React 19.2 or later. On an older version, keep the effect that copies the value and turn the rule off.