Welcome to the EpicWeb.dev Workshop app!

This is the deployed version. Run locally for full experience.

DOM Side-Effects

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.