2024-05-24 작성
Use case of useRef which substitutes for useState
Table of contents
useRef
can store latest data without rendering.
I use useState
almost every case to store state(=data). Because I use these new data to render a page as the updated. But if you use useRef
not useState
, the page would not be rendered even though it has the latest data. Usually this is regarded as a bug(actually this is misunderstanding about useRef).
Has useRef
to be used only for manipulating DOM?
In many cases, useState
is a better hook but if there are following conditions which hook do you want to use?
Situation
- You want to make a page and it will have several steps(page like) components. The page’s data should be submitted when the step is at the end.
- Each step component does not interact each other only parent component(the page) knows it.
- Parent Component has many logics for handling each step component and I want that logics are rendered as little as possible.
useState
based code
// Page.tsx
const Page = () => {
const [route, setRoute] = useState("one"); // Step router
const [parentStore, setParentStore] = useState({ id: "", marketName: "" });
const [parentReview, setParentReview] = useState({ id: { rate: 0, description: "" } });
...other states
const handleRoute = () => {...};
const handleStore = (localData) => {...};
const handleReview = (localData) => {...};
...other logics
const renderChildren = () => {
switch (route) {
case "one":
return <Store handleParentStore={handleStore} />
...
}
}
return (
<main>
{renderChildren()}
</main>
);
};
export default Page;
// Store.tsx
const Store = (handleParentStore, handleRoute) => {
const [childStore, setChildStore] = useState({ id: "", marketName: "" }); // The page's state goes down here.
const handleSubmit = () => {
handleParentStore(childStore) // Propagate local data to the parent
handleRoute(); // Go to next step
};
return (
<div>{...}</div>
);
}
export default Store;
In this case, every data is stored in the each state where it belongs. For example, <Store />
component’s data will be stored in childStore
state. Then its state is propagated through handleSubmit()
. At this point in handleSubmit()
function two different useState hooks are used, setParentStore
, setRoute
. The page would render only once when the step is submitted with this strategy.
Even though setParentStore
and setRoute
is used, why does the page render once?
React has auto batch function from version 18. When it reads event handler, if it has multiple setState
, it deals with all setState
s then generate a render.
useRef
based code
// Page.tsx
const Page = () => {
const [route, setRoute] = useState("one"); // Step router
const storeRef = useRef({ id: "", marketName: "" }); // useState => useRef
const reviewRef = useRef({ id: { rate: 0, description: "" } }); // useState => useRef
...other states // useState => useRef
const handleRoute = () => {...};
const handleStore = () => {...};
const handleReview = () => {...};
...other logics
const renderChildren = () => {
switch (route) {
case "one":
return <Store storeRef={storeRef} {...} /> // useState => useRef
...
}
}
return (
<main>
{renderChildren()}
</main>
);
};
export default Page;
// Store.tsx
const Store = (storeRef, handleRoute) => {
const [store, setStore] = useState({ id: "", marketName: "" }); // The page's state goes down here.
const handleSubmit = () => {
storeRef.current = store; // Propagate local data to the parent
handleRoute(); // Go to next step
};
return (
<div>{...}</div>
);
}
export default Store;
What is different? Only useState
is changed to useRef
in the page. The data which is stored in useRef
will be used only when set a step component’s initial data and communicate with API. So, rendering is no matter in this case.
Conclusion
At the first, I had wondered “Isn’t it possible to optimize rendering counts with useRef
. But that thought was solved with react’s auto batch. Still I believe this strategy can be used. So, if you have any situation to use this strategy for optimization, try out with deep consideration.