See Our Public Workshops:
Chances are you will not need these built-in React Hooks for your most common needs in React. Of course you might have special circumstances so don't take any of this as an assault on your knowledge or code choices if you've found good use for these. This post might help those who are newer to Hooks to stay focused on learning the ones that will have the most impact on their codebase without feeling overwhelmed to learn all of them. If I didn't mention one of React's hooks here, it's probably needed more commonly.
I've categorized these lesser used Hooks by color-code:
- π΄ I doubt you'll ever use this
- π You might use this in rare circumstances
- π’ Depends on what you're building
React's Least Useful Hooks
- π΄ useDebugValue()
- π΄ useInsertionEffect()
- π΄ useImperativeHandle()
- π useSyncExternalStore()
- π useDeferredValue()
- π’ useEffect()
- π’ useLayoutEffect()
useDebugValue()
Do you use the Chrome devtools extension for React? I mean, really use it? Not just have it installed? If you hardly use it like me, then you definitely can move on and I guarantee you don't care about this Hook. I think even if I were to use the extension more, I would still probably not use this Hook that much.
useDebugValue()
lets you create nice labels for your custom hooks in DevTools for debugging reasons, and that's it.
This one makes Kent's top three list of "least used React Hooks":
useInsertionEffect()
I think the docs say it best:
useInsertionEffect is aimed at CSS-in-JS library authors. Unless you are working on a CSS-in-JS library and need a place to inject the styles, you probably want useEffect or useLayoutEffect instead.
I'm sure most of you reading are not maintainers of one of the CSS-in-JS libraries, so you won't be needing this one.
useImperativeHandle()
Sometimes we need useRef
, but its use-cases are more seldom than the popular Hooks. As rare as it is, it's even more rare that we would additionally need "ref-forwarding". Take all those rare use-cases and it's now even more rare that you'll also have use-cases that useImperativeHandle()
solves.
So what is it? If you know what ref-forwarding is, useImperativeHandle()
is a way to limit or customize the exposure to a parent component of the underlying DOM element's API that the ref was forwarded to.
It's almost "bragging rights" if you even find a good reason to use it:
Sarcastically, we all know it's a little weird and we joke about it from time to time:
useSyncExternalStore()
This is one of those Hooks that you might be using indirectly through some third-party library. There could be some decent use-cases to use it from time to time, but I think you'll fine those to be pretty rare.
It's use-cases are listed in the docs as:
- Third-party state management libraries that hold state outside of React.
- Browser APIs that expose a mutable value and events to subscribe to its changes.
So unless you're the maintainer of Redux (hi Mark π), you probably won't need this one. If you are curious based on the use-cases though, I'd recommend this article from "This week in React".
useDeferredValue()
Personally, I have not needed to use this Hook since it became available, but I am intrigued by it. The docs describe its use-cases as:
- Showing stale content while fresh content is loading
- Indicating that the content is stale
- Deferring re-rendering for a part of the UI
This Hooks is all about created a better use experience so technically it's not going to be required for you to get the job done, it just arguably makes some things nicer for users.
It is a "Suspense" related feature. It is a little complex to understand but I think it will become more popular as React developers start to understand and embrace Suspense more. Just be aware of its listed caveats.
useEffect()
I honestly really like these next two Hooks, which isn't always the popular opinion. I think we teach useEffect()
very well in ReactTraining workshops if you are interested in getting to know it better (shameless plug).
For this explanation, we assume you know a little thing or two about how useEffect()
runs. Just to be very clear though, understand these two terms:
- Event Based Side Effect - A side effect that runs because a DOM event occurred, like a button click to update a user.
- Render Phase Based Side Effect - A side effect that runs after render phases. These are the ones that use
useEffect()
, like data-fetching when the component mounts.
In the early days of Hooks, it was commonly thought that useEffect()
should be used for all side effects, whether they were event-based or render-phase-based. For event-based, devs would set state from the event and then respond to the state-change (re-render) by running the effect in useEffect()
.
These days it's considered best practice to let event-based side effects run from the event's function and only use useEffect()
for render-phase based side effects. This new best-practice alone eliminates LOTS of cases where we were jumping through hoops to get useEffect
running for events.
For the cases where we do need useEffect
, by far the most common use-case is data-fetching. Because of the boiler-plate code you'll write for fetching data in useEffect()
, you'll probably end up making an abstraction like a custom Hook. But if you did that, you'd be re-inventing the wheel because there's already really good open source solutions that do a better job of what you'll make. So for render-phase based data-fetching, just use tools such as React Query or you could even try the new loading strategy from React Router that allows you to load in parallel and avoid data-fetching waterfalls.
Summary so far -- this hook has been used less and less over time:
- β Don't use
useEffect()
for event-based side effects - β Don't use
useEffect()
for render-phase data-fetching (Use React Query or React Router Loaders) - β
Use
useEffect()
for render-phase side effects that are not data-fetching (these are more rare)
There is another angle we can take with this conversation. Right now there's a big push to get away from Single Page Applications as being the defacto standard way to use React. Doing SSR and RSC (probably with Remix or Next) means we'll be fetching data and building our UI on the server while also doing client side navigations like an SPA, ie, a hybrid. This drastically reduces the use-cases for useEffect()
because most data-fetching will happen on the server outside of the component.
With these new framework approaches, you'll only need useEffect
for those more rare render-phase side effects that aren't about data fetching (working with window
, document
, localStorage
, etc)
useLayoutEffect()
This one works very similarly to useEffect()
in terms of API but when it runs after renders, it runs just before React paints the UI to the screen whereas useEffect()
runs after the paint. It's use-cases are special and is NOT for data fetching. While the use-cases for useEffect()
are getting smaller over time (see above), useLayoutEffect()
makes the list simply because it's always had rare use-cases.
You'll need useLayoutEffect()
when you want to run a synchronous side effect (probably something with the browser layout) to establish your state before the UI renders.
To start, you can't run synchronous side effects like this next example. It's not allowed because we're doing a side-effect during the render phase (bad for many reasons but also wont work if this is an SSR component):
function MyComp() {// β Bad. Side effect _during_ render phaseconst [state, setState] = useState(window.localStorage.getItem('...'))// ...}
The problem with having null
state and waiting for useEffect
to change the state to be the localStorage
value is that the user will see the results in the UI of one full render and then the UI will change on them based on the new state.
With useLayoutEffect()
we're okay with having that initial null
state because React will finish its first render and will not paint the results to the user until it sees if you set state in useLayoutEffect()
. This Hook runs earlier than useEffect
because useEffect
runs AFTER the paint. When useLayoutEffect()
sets state synchronously, the re-render that occurs will now have the right state from our localStorage
and React will ignore the first render that they never painted and will pain the second render. Thus, the user doesn't see a layout shift.
function MyComp() {// β Good. Side effect after render phase and even though this creates two renders,// React will ignore the first one and only paint the second one.const [state, setState] = useState(null)useLayoutEffect(() => {setState(window.localStorage.getItem('...'))}, [])// ...}
It's use-cases are rare, but it's nice to have when you need it.
Thanks for reading!
Instead of having comments here in our blog, we've tweeted about this post so you can comment there if you wish.
View on Twitter