DOM Side-Effects
Loading "Intro to Dom Side Effects"
Run locally for transcripts
Often when working with React you'll need to interact with the DOM directly. You
may need to use a vanilla-js (non-framework-specific) library that needs to
interact with directly with the DOM. Often to make a UI accessible you need to
consider focus management requiring you to call
.focus()
on an input.Remember that when you do:
<div>hi</div>
that's actually syntactic sugar for a
React.createElement
so you don't actually have access to DOM nodes in your
render method. In fact, DOM nodes aren't created at all until the
ReactDOM.createRoot().render()
method is called. Your component's render
method is really just responsible for creating and returning React Elements and
has nothing to do with the DOM in particular.So to get access to the DOM, you need to ask React to give you access to a
particular DOM node when it renders your component. The way this happens is
through a special prop called
ref
.There are two ways to use the
ref
prop. A callback and the useRef
hook.ref callback:
The simplest way is to use the
ref
prop is by passing a callback:function MyDiv() {
return (
<div
ref={myDiv => {
console.log(`here's my div!`, myDiv)
return function cleanup() {
console.log(`my div is getting removed from the page!`, myDiv)
}
}}
>
Hey, this is my div!
</div>
)
}
This is the preferred approach.
For backward compatibility reasons, TypeScript will tell you that myDiv can be
a
HTMLDivElement
or null
. So you may need to handle the null
case
(normally, just return early). In the future, it will never be null
.ref object:
For a more complex use case (like you need to interact with the DOM after the
initial render) you can use the
useRef
hook.Here's a simple example of using the
ref
prop with useRef
:function MyDiv() {
const myDivRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const myDiv = myDivRef.current
// myDiv is the div DOM node!
console.log(myDiv)
}, [])
return <div ref={myDivRef}>hi</div>
}
The benefit of this API over the
ref
callback approach is that you can
store the ref object in a variable and safely access it later within a
useEffect
callback or event handlers.After the component has been rendered, it's considered "mounted." That's when
the useEffect callback is called and so by that point, the ref should have
its
current
property set to the DOM node. So often you'll do direct DOM
interactions/manipulations in the useEffect
callback.Every element has a special
ref
prop (as shown above). You pass a ref to that
prop and React will give you a reference to the thing that's created for that
element.You can also pass
ref
to a function component and that component can forward
the ref
onto another element, or it can add handy methods onto it using
useImperativeHandle
which we'll cover in the "Advanced React APIs" workshop.📜 Learn more about
useRef
from the docs:
https://react.dev/reference/react/useRef🦉 Note, sometimes the DOM interaction will make observable changes to the UI.
In those cases you'll want to use
useLayoutEffect
and we cover that in the
"Advanced React APIs" workshop.🦉 A ref is basically state that's associated to a React component that will not
trigger a rerender when changed. So you can store whatever you'd like in a ref,
not just DOM nodes.
Keep in mind, React can't track when you change a ref value. That's part of it's
appeal in some cases and it can cause trouble in others. You'll develop the
intuition of when to use which over time, but in general it's best to start with
state if you're unsure and then move to a ref if you decide you don't want a
rerender when it's updated. We'll dive deeper into non-DOM refs in future
workshops.