Welldone Software
Published in

Welldone Software

useCallback Might Be What You Meant By useRef & useEffect

Sometimes we respond to React element mounts. The first instinct is to useRef & useEffect. But It’s wrong. What you really need is often useCallback.

setRef returned from useCallback

If you want to respond to a React element’s mounting on the DOM, you may be tempted to use useRef to get a reference to it and useEffect to respond to its mounts and unmounts. But it won’t work.

This is because there is no callback or re-render when a component is (un)mounted and attached to ref.current using useRef.

The react-hooks eslint rules would even warn you about that. Notice how neither ref nor ref.current as deps of useEffect trigger it.

react-hooks warns about using useRef with useEffect

See this for yourself in the sandbox:

A sandbox where useRef as deps of useEffect fails to trigger it on time

So what can we do?

useCallback

(link to the official React docs regarding this)

We can rely on passing a regular function wrapped with useCallback to ref and react to the latest DOM node reference that it returns.

You can try it in the following sandbox:

A sandbox where useCallback is used as a ref and detects renders on time

Caveat: theref function is guaranteed to be called on mounts and unmounts of elements, even on the first mount, and even if the unmount is as a result of the parent element unmounting.

Caveat2: Make sure to wrap ref callbacks with useCallback.

Without it, if you cause a render from the ref callback, the ref callback will be triggered again with null, potentially leading to an endless loop, because of React internals.

To play with this, try removing useCallback from the sandbox above and see this other sandbox.

This pattern can be used in several more ways:

useState

Since useState is a function that is consistent between renders, it can also be used as a ref.

In this case, the whole node will be saved in its own state.

As a state, when it changes, it would trigger a re-render and the state can be safely used in the render’s results and as a useEffect dependency:

useStateRef

Accessing the DOM is expensive, so we want to do this as little as possible.

If you don’t need the whole node like in the previous hook, you better save only part of it on a state:

As you can see we access the DOM only when the element to which we pass ref is mounted and only save clientHeight in this stage.

useRefWithCallback

But sometimes, for the sake of performance, you can do without triggering re-renders on mounts and unmounts of the element that you use it’s ref.

The following hook doesn't save the node on the state. Instead of using state, it responds to mounts and unmounts directly, so it doesn’t trigger any re-renders.

In the end, if you understand the principle behind using useCallback as an element ref you might come up with your own ideas tailored for your specific needs.

Happy Hooking :D

--

--

--

The leading full-stack software solutions company. Creating amazing frontends and rock-solid backends using top-notch technologies and practices. Visit us at https://welldone.software/

Recommended from Medium

How to create Routing with Lit Library

Create GraphQL API in Expressjs Project Using Apollo Server Express

How to implement Linkedin OAuth 2.0 Login in Ionic 4

First Steps in gRPC Bindings for React Native

Typescript is great!

Data types in TypeScript. The Complete TypeScript Developer Course 2022 s.

Do Not Burn Your CPU Working With CSS Animations

Creating AnimatedVectorDrawables with Shape Shifter, Part II

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Vitali Zaidman

Vitali Zaidman

https://twitter.com/vzaidman ⎝(•ω•)⎠ https://vzaidman.com

More from Medium

Now You’re Thinking With (React) Portals

Doing React Twice at Flatiron School

From Redux Sagas to React Query

Take care of internet traffic experience. Make cancelable requests with react-hooks.