I want to set admin object in store. Root component is re-render every time components are change. So I use useEffect with empty dependency array []. But I got eslint warning missing dependencies admin and setAdmin. So I add this to the dependency array.
**My confusion is: **
At first, I think useEffect will re-render every time components are change because I add admin and setAdmin as its dependencies and admin object is not the same each time because object is reference.
But, when I test it out, useEffect render once. I really confuse about this.
Can you explain me how i am misunderstanding?
Thanks
import Sidebar from "@/components/side-bar"
import { IAdmin, useAdminStore } from "@/store"
import { useEffect } from "react"
import { Outlet, useLoaderData } from "react-router-dom"
export default function Root() {
const { admin } = useLoaderData() as { admin: IAdmin }
const { setAdmin } = useAdminStore()
useEffect(() => setAdmin(admin), [admin, setAdmin])
return (
<div className="flex min-h-screen w-full gap-8">
<Sidebar />
<main className="flex-1 p-4">
<Outlet />
</main>
</div>
)
}
2
Answers
In React, useEffect is used to perform side effects in function components. When you specify an array of dependencies, the effect will only re-run if one of those dependencies has changed since the last render.
Your code snippet uses useEffect to update the admin state using setAdmin whenever admin or setAdmin changes. The ESLint warning you saw about missing dependencies is meant to ensure that your effect always has the most up-to-date values for admin and setAdmin.
consider the following points:
Check if admin Actually Changes: Ensure that the admin object from useLoaderData() changes as expected. If it doesn’t change between renders, useEffect won’t be triggered again.
Constant setAdmin Function: The state update functions from hooks like useState or context (as with your useAdminStore) usually don’t change across renders, so you might not need to include setAdmin in your dependency array.
Performance Optimization: If concerned about performance, particularly if admin is a large or complex object, consider using deep comparison to determine if admin has actually changed before calling setAdmin. This can be achieved with custom logic or libraries like lodash.
Use of useMemo or useCallback: If admin is recreated on every render (e.g., from an API call in a parent component), wrap its creation in useMemo or the update function in useCallback to ensure they maintain the same reference unless their dependencies change.
If the problem persists after reviewing these points, more information about how and when admin changes and the setup of useAdminStore would be helpful for a more precise diagnosis and solution.
First of all, your "terminology" is a little bit wrong.
useEffect
doesn’t re-render. It runs the effect, which is the function you pass into theuseEffect
hook. Components rerender, and if the dependencies are different from the last render, then the effect is run.Second, if
setAdmin
is returned by something likeuseState
or redux, or a decent state management library, it will be referentially stable. Meaning it will have the same reference in every render, so you can safely add it to the dependency list ofuseEffect
,useMemo
,useCallback
, etc.Finally coming to your question. Your effect is not running multiple times because
admin
is simply the same object, sinceuseLoaderData
returns a stable object, ie it doesn’t change between renders, unless the loader function is called again for some reason. And since you are setting the state to the same thing, react doesn’t rerender the component and the effect doesn’t run again.