I am working on a React coding challenge (problem link) and the solution is here.
In the problem, the custom hook returns true for the first render, otherwise returns false.
import { useRef } from "react";
export function useIsFirstRender(): boolean {
const isFirstRender = useRef(true);
if (isFirstRender.current) {
isFirstRender.current = false;
return true;
}
return false;
}
In the codes above, I don't get the way to declare this function. What exactly useIsFirstRender(): boolean {} means?
And since isFirstRender.current is defined as true in the function, It sounds like it will return true at any renders.
Do I misunderstand the usage of useRef? Thank you!
I don't get the way to declare this function. What exactly
useIsFirstRender(): boolean {}
means?
That's TypeScript code, not JavaScript code. The : boolean
part means it returns a boolean
value.
And since
isFirstRender.current
is defined astrue
in the function, It sounds like it will returntrue
at any renders.
isFirstRender.current
isn't always true
. useRef
gives you a mutable ref object, and it always gives you the same mutable ref object on every call (for that useRef
in that hook/component). (The ref object is stored in the instance information of the component this hook is being called by.) In the first call, it initializes the ref object's current
property with the value you provide to useRef
, but it only does that during the first call, not subsequent calls. If you can change the current
value (as you see that code does), that change endures from call to call. That's one of the points of refs: They let you store instance-specific, durable information connected to the lifecycle of the element the function component (or the hooks it calls) are managing.
Here's an example with some logging that may help clarify it (using just JavaScript, not TypeScript):
const { useState, useRef } = React;
function useIsFirstRender(): boolean {
console.log(`useIsFirstRender called`);
const isFirstRender = useRef(true);
console.log(`isFirstRender.current = ${isFirstRender.current}`);
if (isFirstRender.current) {
console.log(`First render; setting isFirstRender.current to false`);
isFirstRender.current = false;
return true;
}
console.log(`Not first render`);
return false;
}
const Example = () => {
console.log(`Example called`);
const isFirst = useIsFirstRender();
const [counter, setCounter] = useState(0);
return (
<div>
<div>isFirst: {isFirst}</div>
<div>
Counter: {counter}{" "}
<input type="button" value="+" onClick={() => setCounter((c) => c + 1)} />
</div>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
Notice how the isFirstRender.current
value is only true
the first time useIsFirstRender
is called for the mounted element, not for subsequent calls.