Most developers agree that initializing data or setting up subscriptions in React components can be a bit tricky, especially when ensuring code runs at the right moment during the component lifecycle.
This blog post will make things easier for you. We'll walk you through a simple way to run code when your React components first appear - no more scratching your head or staying up late reading React docs.
We'll guide you through each step with some easy-to-follow tips that will make your coding life much smoother. By the time you finish reading, you'll be running code on mount like it's no big deal.
Input focus on mount
Suppose we have the following code:
function App() {
const [
searchTerm,
setSearchTerm,
] = React.useState('');
return (
<main>
<form>
{/* I want to focus this input on mount: */}
<input
value={searchTerm}
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<button>Search</button>
</form>
</main>
);
}
We can capture a reference to that input with the useRef
hook:
function App() {
const [
searchTerm,
setSearchTerm,
] = React.useState('');
const inputRef = React.useRef();
return (
<main>
<form>
<input
ref={inputRef}
value={searchTerm}
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<button>Search</button>
</form>
</main>
);
}
Input DOM nodes have a .focus()
method we can call to focus it, but how do I do it on mount? I can try to do it right in the render:
function App() {
const [
searchTerm,
setSearchTerm,
] = React.useState('');
const inputRef = React.useRef();
inputRef.current.focus();
// ✂️
}
Unfortunately, this leads to an error:
The trouble is, when we first create the inputRef
, it's empty { current: undefined }
. It only captures the input DOM node after the first render.
The solution is to use the useEffect
hook:
function App() {
const [
searchTerm,
setSearchTerm,
] = React.useState('');
const inputRef = React.useRef();
React.useEffect(() => {
inputRef.current.focus();
}, []);
// ✂️
}
Critically, we're passing an empty dependency array. This is how we tell React, “this effect doesn't depend on any other values”. And if it doesn't depend on any values, it'll never re-run!
Effects always run after the first render, and then again whenever the dependencies change. This structure ensures it'll only run after the first render.
HTML form inputs have an
autofocus
property that can be used to automatically focus the element on page load: <input autofocus type="text"/>
Given that there's a built-in way to autofocus an input, why are we going through all the trouble with
useRef
and useEffect
??Unfortunately, it isn't safe to use the
autofocus
attribute in React.The
autofocus
attribute only works reliably if the element is present when the page first loads. It won't work if the element is dynamically injected into the page afterwards. And in React, pretty much every element is dynamically injected! The only exception is if you use server-side rendering, and even then, only for the very first page the user visits on your site.And so, the solution shown above, capturing an input with a ref and triggering
.focus()
in an effect, is the best way to solve this problem in React.Mouse subscription
Let's suppose we want to track the user's cursor position. Whenever they move their mouse, we'll update some state.
We can add onMouseMove
event handlers to specific DOM nodes, like this:
<div
onMouseMove={event => {
setMousePosition({
x: event.clientX,
y: event.clientY,
});
}}
>
Spend a few moments tinkering, if you'd like, to see if you can come up with a solution.
In order to listen for global events, you can use
window.addEventListener
. Specifically, you'll want to listen for mousemove
events. You can get the cursor position using event.clientX
and event.clientY
.A sandbox is provided below:
To listen for global mouse-move events, we need to use window.addEventListener
. We can register it in an effect hook, like this:
React.useEffect(() => {
function handleMouseMove(event) {
setMousePosition({
x: event.clientX,
y: event.clientY,
});
}
window.addEventListener('mousemove', handleMouseMove);
});
At first glance, this seems to work, but there are two problems with this approach.
First, we aren't ever cleaning up our event listener. Second, the issue I want to talk about is that we're adding multiple event listeners.
Because we haven't specified a dependency array, this effect will run after every single render. That means every time the user's mouse position changes, we call window.addEventListener
again. If 100 mouse-move events fire, we'll have 100 event listeners.
window.addEventListener
is a subscription.
We only want to subscribe once, when the component first mounts.
Here's what the proper solution looks like:
React.useEffect(() => {
function handleMouseMove(event) {
setMousePosition({
x: event.clientX,
y: event.clientY,
});
}
window.addEventListener('mousemove', handleMouseMove);
}, []);
window.addEventListener
is not part of React, it's part of the DOM. When we call this method, we set up a long-running process that will call our callback function whenever the mousemove
event is detected.
It's the same story with many other situations, like:
- Running an interval
- Opening a web socket connection
- Using
ResizeObserver
I have some illustrations that show visually what's going on here.
With an empty dependency array, the effect only runs once, after the first render, starting a single long-running process:

Without the empty dependency array, however, our effect runs after every render, starting multiple long-running processes:

This is what the final solution will look like:
Fortunately, I wrote an article on March 2025 on How to Cleanup React Component. Check it out to see how to avoid common issue that come from not removing events after component unmount.
Conclusion
Running code on mount in React is a powerful technique that can significantly enhance your applications when used correctly.
Whether you're focusing input elements, handling global events, or managing subscriptions, understanding when and how to leverage the useEffect
hook with an empty dependency array is crucial.
Remember these key takeaways:
- Use
useEffect
with an empty deps array to run code only once after the first render - Always clean up subscriptions in the effect's return function to prevent memory leaks
- Be intentional about your dependency arrays to avoid creating multiple subscriptions
- Refs are perfect companions when you need to interact with DOM elements directly
By implementing these patterns correctly, you'll create more responsive and efficient React applications while avoiding common pitfalls like performance issues or memory leaks.
I hope that the next time you need to run code on component mount, you'll know exactly how to approach it with confidence.
🔍. Similar posts
React Component Cleanup
12 Mar 2025
Best Practices for Using the useEffect Hook in React
22 Feb 2025
How to Effectively Use the React useId Hook in Your Apps
16 Feb 2025