Sometimes you need to know "is this the mount, or a re-render?" — to skip a validation on the initial paint, avoid firing an analytics event before the user has done anything, or run an animation only after the first frame. useIsFirstRender answers that in one boolean: true on the very first render of a component, false on every render after.
Implement useIsFirstRender(). It takes no arguments and returns a boolean. The flip from true to false must happen during render — no extra re-render, no waiting for an effect — and once it's false it stays false for the life of the component.
function useIsFirstRender() {
// returns true on the first render, false afterwards
}
function Search({ query }) {
const isFirst = useIsFirstRender();
useEffect(() => {
if (isFirst) return; // skip the fetch on mount
runSearch(query);
}, [query]);
}
const isFirst = useIsFirstRender();
// render 1 -> true
// render 2 -> false
// render 3 -> false
true and the second already sees false.true. Once false, always false.You'll keep a mutable ref that starts true, return its value, and flip it to false in the same render — so the first render reports true and every render after reports false.
React re-runs a component's function on every render, but the function itself can't tell which run is the first — each call starts fresh. You need one bit of memory that survives across renders and can be read-then-updated within a render without scheduling another. That's exactly what a ref is for: an object whose .current persists between renders and whose mutation doesn't trigger one.
Think of a light switch that's wired to flip itself the instant you look at it. The first render peeks: the switch reads true, and the act of reading flips it to false. Every later render peeks and sees false — it's already been flipped. Because a ref mutation doesn't cause a re-render, this "read then flip" happens invisibly, inline, with no extra render cost.
The instinct is to reach for state and an effect:
function useIsFirstRenderNaive() {
const [isFirst, setIsFirst] = useState(true);
useEffect(() => {
setIsFirst(false);
}, []);
return isFirst;
}
This is off by a render and wasteful. The effect runs after the first render commits, so it schedules a second render just to flip the flag — the component renders twice on mount, and any logic reading isFirst during that first render is correct, but you've paid for an extra render and a state update to get there. Worse, code that runs synchronously between the first render and the effect still sees true. A ref avoids the whole detour.
const { useRef } = require('react');
function useIsFirstRender() {
const isFirst = useRef(true);
if (isFirst.current) {
isFirst.current = false; // flip DURING this render
return true; // ...but report true for this first one
}
return false;
}
module.exports = { useIsFirstRender };
The ref is seeded true and persists across renders untouched by re-renders. On the first render, isFirst.current is true: we immediately set it to false (so future renders won't take this branch) and return true for the current render. Every render afterward finds .current already false and returns false. Mutating a ref doesn't schedule a render, so there's no double-render and no state churn — the value is correct synchronously, in the same pass that reads it.
Mount a component that calls useIsFirstRender(), then re-render it twice:
useRef(true) creates { current: true }. The if is taken: set .current = false, return true. The component sees isFirst === true.useRef returns the same ref, now { current: false }. The if is skipped; return false.false; return false.No effect ran, no extra render was scheduled — the flip happened inline on render 1.
true. A ref flips inline..current = false before you return true.false for the component's life.useRef, so instances don't interfere; a module-level flag would (wrongly) be global.useFirstMountState — the same pattern under a different name in react-use; recognizing it helps you read library source.useEffect so it runs on updates only, which generalizes into useUpdateEffect.true then false), the observable result is still correct, but it's worth knowing why you might see two renders.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
Sometimes you need to know "is this the mount, or a re-render?" — to skip a validation on the initial paint, avoid firing an analytics event before the user has done anything, or run an animation only after the first frame. useIsFirstRender answers that in one boolean: true on the very first render of a component, false on every render after.
Implement useIsFirstRender(). It takes no arguments and returns a boolean. The flip from true to false must happen during render — no extra re-render, no waiting for an effect — and once it's false it stays false for the life of the component.
function useIsFirstRender() {
// returns true on the first render, false afterwards
}
function Search({ query }) {
const isFirst = useIsFirstRender();
useEffect(() => {
if (isFirst) return; // skip the fetch on mount
runSearch(query);
}, [query]);
}
const isFirst = useIsFirstRender();
// render 1 -> true
// render 2 -> false
// render 3 -> false
true and the second already sees false.true. Once false, always false.You'll keep a mutable ref that starts true, return its value, and flip it to false in the same render — so the first render reports true and every render after reports false.
React re-runs a component's function on every render, but the function itself can't tell which run is the first — each call starts fresh. You need one bit of memory that survives across renders and can be read-then-updated within a render without scheduling another. That's exactly what a ref is for: an object whose .current persists between renders and whose mutation doesn't trigger one.
Think of a light switch that's wired to flip itself the instant you look at it. The first render peeks: the switch reads true, and the act of reading flips it to false. Every later render peeks and sees false — it's already been flipped. Because a ref mutation doesn't cause a re-render, this "read then flip" happens invisibly, inline, with no extra render cost.
The instinct is to reach for state and an effect:
function useIsFirstRenderNaive() {
const [isFirst, setIsFirst] = useState(true);
useEffect(() => {
setIsFirst(false);
}, []);
return isFirst;
}
This is off by a render and wasteful. The effect runs after the first render commits, so it schedules a second render just to flip the flag — the component renders twice on mount, and any logic reading isFirst during that first render is correct, but you've paid for an extra render and a state update to get there. Worse, code that runs synchronously between the first render and the effect still sees true. A ref avoids the whole detour.
const { useRef } = require('react');
function useIsFirstRender() {
const isFirst = useRef(true);
if (isFirst.current) {
isFirst.current = false; // flip DURING this render
return true; // ...but report true for this first one
}
return false;
}
module.exports = { useIsFirstRender };
The ref is seeded true and persists across renders untouched by re-renders. On the first render, isFirst.current is true: we immediately set it to false (so future renders won't take this branch) and return true for the current render. Every render afterward finds .current already false and returns false. Mutating a ref doesn't schedule a render, so there's no double-render and no state churn — the value is correct synchronously, in the same pass that reads it.
Mount a component that calls useIsFirstRender(), then re-render it twice:
useRef(true) creates { current: true }. The if is taken: set .current = false, return true. The component sees isFirst === true.useRef returns the same ref, now { current: false }. The if is skipped; return false.false; return false.No effect ran, no extra render was scheduled — the flip happened inline on render 1.
true. A ref flips inline..current = false before you return true.false for the component's life.useRef, so instances don't interfere; a module-level flag would (wrongly) be global.useFirstMountState — the same pattern under a different name in react-use; recognizing it helps you read library source.useEffect so it runs on updates only, which generalizes into useUpdateEffect.true then false), the observable result is still correct, but it's worth knowing why you might see two renders.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.