react.js

3 Common Mistakes to Avoid When Handling Events in React

In React apps, event listeners or observers perform certain actions when specific events occur. While it’s quite easy to create event listeners in React, there are common pitfalls you need to avoid to prevent confusing errors. These mistakes are made most often by beginners, but it’s not rare for them to be the reason for one of your debugging sessions as a reasonably experienced developer.

In this article, we’ll be exploring some of these common mistakes, and what you should do instead.

1. Accessing state variables without dealing with updates

Take a look at this simple React app. It’s essentially a basic stopwatch app, counting up indefinitely from zero.

JavaScript
import { useState, useEffect } from 'react'; export default function App() { const [time, setTime] = useState(0); useEffect(() => { const timer = setInterval(() => { setTime(time + 1); }, 1000); return () => { window.clearInterval(timer); } }, []); return ( <div> Seconds: {time} </div> ); }

However, when we run this app, the results are not what we’d expect:

The seconds is stuck at 1.
Stuck at 1

This happens because the time state variable being referred to by the setInterval() callback/closure refers to the stale state that was fresh at the time when the closure was defined.

The closure is only able to access the time variable in the first render (which had a value of 0) but can’t access the new time value in subsequent renders. JavaScript closure remembers the variables from the place where it was defined.

The issue is also due to the fact that the setInterval() closure is defined only once in the component.

The time variable from the first render will always have a value of 0, as React doesn’t mutate a state variable directly when setState is called, but instead creates a new variable containing the new state. So when the setInterval closure is called, it only ever updates the state to 1.

Here are some ways to avoid this mistake and prevent unexpected problems.

1. Pass function to setState

One way to avoid this error is by passing a callback to the state updater function (setState) instead of passing a value directly. React will ensure that the callback always receives the most recent state, avoiding the need to access state variables that might contain old state. It will set the state to the value the callback returns.

Here’s how we apply this for our example:

JavaScript
import { useState, useEffect } from 'react'; export default function App() { const [time, setTime] = useState(0); useEffect(() => { const timer = setInterval(() => { // 👇 Pass callback setTime((prevTime) => prevTime + 1); }, 1000); return () => { window.clearInterval(timer); } }, []); return ( <div> Seconds: {time} </div> ); }
The time is increased by 1 every second - success.
The time is increased by 1 every second – success.

Now the time state will be incremented by 1 every time the setInterval() callback runs, just like it’s supposed to.

2. Event listener re-registration

Another solution is to re-register the event listener with a new callback every time the state is changed, so the callback always accesses the fresh state from the enclosing scope.

We do this by passing the state variable to useEffect‘s dependencies array:

JavaScript
import { useState, useEffect } from 'react'; export default function App() { const [time, setTime] = useState(0); useEffect(() => { const timer = setInterval(() => { setTime(time + 1); }, 1000); return () => { window.clearInterval(timer); } }, [time]); return ( <div> Seconds: {time} </div> ); }

Every time the time state is changed, a new callback accessing the fresh state is registered with setInterval(). setTime() is called with the latest time state added to 1, which increments the state value.

2. Registering event handler multiple times

This is a mistake frequently made by developers new to React hooks and functional components. Without a basic understanding of the re-rendering process in React, you might try to register event listeners like this:

JavaScript
import { useState } from 'react'; export default function App() { const [time, setTime] = useState(0); setInterval(() => { setTime((prevTime) => prevTime + 1); }, 1000); return ( <div> Seconds: {time} </div> ); }

Or you might put it in a useEffect hook like this:

JavaScript
import { useState } from 'react'; export default function App() { const [time, setTime] = useState(0); useEffect(() => { setInterval(() => { setTime((prevTime) => prevTime + 1); }, 1000); }); return ( <div> Seconds: {time} </div> ); }

If you do have a basic understanding of this, you should be able to already guess what this will lead to on the web page.

The seconds keep accelerating.
It eventually gets as bad as this.

What’s happening?

What’s happening is that in a functional component, code outside hooks, and outside the returned JSX markup is executed every time the component re-renders.

Here’s a basic breakdown of what happens in a timeline:

  1. 1st render: listener 1 registered
  2. 1 second after listener 1 registration: time state updated, causing another re-render)
  3. 2nd render: listener 2 registered.
  4. Listener 1 never got de-registered after the re-render, so…
  5. 1 second after last listener 1 call: state updated
  6. 3rd render: listener 3 registered.
  7. Listener 2 never got de-registered after the re-render, so…
  8. 1 second after listener 2 registration: state updated
  9. 4th render: listener 4 registered.
  10. 1 second after last listener 1 call: state updated
  11. 5th render: listener 5 registered.
  12. 1 second after last listener 2 call: state updated
  13. 6th render: listener 6 registered.
  14. Listener 3 never got de-registered after the re-render, so…
  15. 1 second after listener 3 registration: state updated.
  16. 7th render: listener 7 registered…

Eventually, things spiral out of control as hundreds and then thousands (and then millions) of callbacks are created, each running at different times within the span of a second, incrementing the time by 1.

The fix for this is already in the first example in this article – put the event listener in the useEffect hook, and make sure to pass an empty dependencies array ([]) as the second argument.

JavaScript
import { useEffect, useState } from 'react'; export default function App() { const [time, setTime] = useState(0); useEffect(() => { setInterval(() => { setTime((prevTime) => prevTime + 1); }, 1000); }, []); return ( <div> Seconds: {time} </div> ); }

useEffect runs after the first render and whenever any of the values in its dependencies array change, so passing an empty array makes it run only on the first render.

The time increases steadily, but by 2.
The time increases steadily, but by 2.

The time increases steadily now, but as you can see in the demo, it goes up by 2 seconds, instead of 1 second in our very first example. This is because in React 18 strict mode, all components mount, unmount, then mount again. so useEffect runs twice even with an empty dependencies array, creating two listeners that update the time by 1 every second.

We can fix this issue by turning off strict mode, but we’ll see a much better way to do so in the next section.

3. Not unregistering event handler on component unmount.

What happened here was a memory leak. We should have ensured that any created event listener is unregistered when the component unmounts. So when React 18 strict mode does the compulsory unmounting of the component, the first interval listener is unregistered before the second listener is registered when the component mounts again. Only the second listener will be left and the time will be updated correctly every second – by 1.

You can perform an action when the component unmounts by placing in the function useEffect optionally returns. So we use clearInterval to unregister the interval listener there.

JavaScript
import { useEffect, useState } from 'react'; export default function App() { const [time, setTime] = useState(0); useEffect(() => { console.log('here'); const timer = setInterval(() => { setTime((prevTime) => prevTime + 1); }, 1000); // 👇 Unregister interval listener return () => { clearInterval(timer); } }, []); return ( <div> Seconds: {time} </div> ); }

useEffect‘s cleanup function runs after every re-render, not only when the component unmounts. This prevents memory leaks that happen when an observable prop changes value without the observers in the component unsubscribing from the previous observable value.

Conclusion

Creating event listeners in React is pretty straightforward, you just need to be aware of these caveats, so you avoid unexpected errors and frustrating debugging spells. Avoid accessing stale state variables, don’t register more event listeners than required, and always unregister the event listener when the component unmounts.

How to Capitalize the First Letter of Each Word in React

To capitalize the first letter of each word in a string in React:

  1. Split the string into an array of words with .split('').
  2. Iterate over the words array with .map().
  3. For each word, return a new word that is an uppercase form of the word’s first letter added to the rest of the word, i.e., word.charAt(0).toUpperCase() + word.slice(1).
  4. Join the words array into a string with .join(' ').

For example:

App.js
export default function App() { const capitalizeWords = (str) => { return str .toLowerCase() .split(' ') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); }; const str1 = 'coding BEAUTY'; const str2 = 'LEARNING javascript'; return ( <div> <b>{str1}</b> <br /> Capitalized: <b>{capitalizeWords(str1)}</b> <br /> <br /> <b>{str2}</b> <br /> Capitalized: <b>{capitalizeWords(str2)}</b> </div> ); }
Strings, along with the result of capitalizing the first letter of each of their words.
Strings, along with the result of capitalizing the first letter of each of their words.

The capitalizedWords() function takes a string and returns a new string with all the words capitalized.

First, we use the toLowerCase() method to lowercase the entire string, ensuring that only the first letter of each word is uppercase.

JavaScript
// coding beauty console.log('coding BEAUTY'.toLowerCase());

Tip: If it’s not necessary for the remaining letters in each word to be lowercase, you can remove the call to the toLowerCase() method.

Then we call the String split() method on the string to split all the words into an array.

JavaScript
// [ 'welcome', 'to', 'coding', 'beauty' ] console.log('welcome to coding beauty'.split(' '));

After creating the array of words, we call the map() method on it, with a callback function as an argument. This function is called by map() and returns a result for each word in the array.

In the function, we get the word’s first character with charAt(), convert it to uppercase with toUpperCase(), and concatenate it with the rest of the string.

We use the String slice() method to get the remaining part of the string. Passing 1 to slice() makes it return the portion of the string from the second character to the end.

Note: String (and array) indexing is zero-based JavaScript, so the first character in a string is at index 0, the second at 1, and the last at str.length-1

Lastly, we concatenate the words into a single string, with the Array join() method

Passing a space (' ') to join() separates the words by a space in the resulting string.

JavaScript
// Welcome To Coding Beauty console.log(['Welcome', 'To', 'Coding', 'Beauty'].join(' '));

After creating the capitalizeWords() function, we call it as the component is rendered by wrapping it in curly braces ({ }) in our JSX code.

App.js
return ( <div> <b>{str1}</b> <br /> Capitalized: <b>{capitalizeWords(str1)}</b> <br /> <br /> <b>{str2}</b> <br /> Capitalized: <b>{capitalizeWords(str2)}</b> </div> );

The function is invoked and its result is rendered at the point where the curly braces are located.

How to Change an Element’s Style on Hover in React

To change an element’s style on hover in React, set a className on the element, and style its :hover pseudo-class.

For example:

App.js
import './App.css'; export default function App() { return ( <div> <div className="box"> <p>Coding Beauty</p> </div> </div> ); }
App.css
.box { height: 100px; width: 100px; background-color: blue; color: white; font-weight: bold; padding: 8px; text-align: center; } .box:hover { background-color: yellow; color: black; }
Hovering over the element changes its style.
Hovering over the element changes its style.

We use the :hover pseudo-class to style an element when the user hovers over it with the mouse pointer.

Change element style on hover with inline styling

We can also change an element’s style on hover using inline styles and the element’s style prop. To do this, we need to create state that will determine whether the hover styles should be applied to the element or not. We also need to add event listeners for the mouseenter and mouseleave and change the state’s value in them.

For example:

App.js
import { useState } from 'react'; export default function App() { const [hover, setHover] = useState(false); const handleMouseEnter = () => { setHover(true); }; const handleMouseLeave = () => { setHover(false); }; return ( <div> <div style={{ height: '100px', width: '100px', backgroundColor: hover ? 'yellow' : 'blue', color: hover ? 'black' : 'white', fontWeight: 'bold', padding: '8px', textAlign: 'center', }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > <p>Coding Beauty</p> </div> </div> ); }

We use the useState hook to create the boolean state variable that will determine whether the hover styles should be applied to the element or not. useState returns an array of two values. The first is a variable that stores the state, and the second is a function that updates the state when it is called.

We use the onMouseEnter prop to listen for the mouseenter event to detect when the mouse enters within the element’s bounds.

Note: While we could also listen for the mouseover event to detect hover, this event is triggered on an element and every single one of its ancestor elements in the DOM tree (i.e. it bubbles) and this could cause serious performance problems in deep hierarchies. mouseenter doesn’t bubble so we can use it without worrying about this.

Similarly, we use the onMouseLeave prop to listen for the mouseleave to detect when the mouse leaves the element’s bounds.

We use the ternary operator to conditionally set the style based on the boolean state.

Change element style on hover with custom component

If you frequently use the inline styles approach to change the element’s style on hover, it will be better if you encapsulate the logic into a custom component, so you can reuse it in multiple places in your codebase and avoid unnecessary duplication.

Here’s what such a component would like:

Hover.jsx
import { useState } from 'react'; export default function Hover({ children }) { const [hover, setHover] = useState(); const handleMouseEnter = () => { setHover(true); }; const handleMouseLeave = () => { setHover(false); }; return ( <div style={{ display: 'contents' }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > {children(hover)} </div> ); }

The Hover component takes a callback function as its child. It passes the state variable storing the hover state to this callback so that you can use it to change the style of the element returned from the callback. Hover calls the callback to render this element.

It wraps the element with a div, on which it listens for the mouseenter and mouseleave events to update the state variable. We set the display CSS property to contents on the wrapper because it plays no visual role on the page. It’s only there for the hover detection.

Here’s how we can use our custom Hover component.

Hover.jsx
import Hover from './Hover'; export default function App() { return ( <div> <Hover> {(hover) => ( <div style={{ height: '100px', width: '100px', backgroundColor: hover ? 'yellow' : 'blue', color: hover ? 'black' : 'white', fontWeight: 'bold', padding: '8px', textAlign: 'center', }} > <p>Coding Beauty</p> </div> )} </Hover> </div> ); }

How to Get an Input Field’s Value in React

To get the value of an input field in React:

  1. Create a state variable to store the input field’s value.
  2. Set an onChange event handler on the input field.
  3. In the event handler, assign event.target.value to the state variable.
  4. The state variable will contains the input field’s value at any given time.

For example:

App.js

import { useState } from 'react';

export default function App() {
  const [message, setMessage] = useState('');

  const [updated, setUpdated] = useState(message);

  const handleChange = (event) => {
    setMessage(event.target.value);
  };

  const handleClick = () => {
    //  "message" stores input field value
    setUpdated(message);
  };

  return (
    <div>
      <input
        type="text"
        id="message"
        name="message"
        onChange={handleChange}
        value={message}
      />

      <h2>Message: {message}</h2>

      <h2>Updated: {updated}</h2>

      <button onClick={handleClick}>Update</button>
    </div>
  );
}

With the useState hook, we create a state variable (message) to store the input field’s current value. We also create another state variable (updated) that will be updated with the input field value when the button is clicked.

We set an onChange event handler on the input field to execute an action. In the handler, we use the Event object’s target property to access the object representing the input element. The value property of this object contains the input value, so we pass it to setMessage to update message, and this reflects on the page.

After setting up the controlled input, we can now use message outside the handleChange handler to get the current value of the input field.

So in the onClick event handler we set on the button, we use setUpdated(message) to update the updated variable with the input field’s current value.

Get input value with ref

Alternatively, we can use a ref to get the value of an input field in React. After setting the ref on the input, we’ll be able to use the ref object to access the input field’s current value in the event handler.

App.js

import { useRef, useState } from 'react';

export default function App() {
  const inputRef = useRef(null);

  const [updated, setUpdated] = useState('');

  const handleKeyDown = (event) => {
    if (event.key === 'Enter') {
      setUpdated(inputRef.current.value);
    }
  };

  return (
    <div>
      <input
        ref={inputRef}
        type="text"
        id="message"
        name="message"
        onKeyDown={handleKeyDown}
      />

      <h2>Updated: {updated}</h2>
    </div>
  );
}

We set an onKeyDown event listener on the input to perform an action when a key is pressed. In this listener, we use the KeyboardEvent object’s key property to check if the key pressed is Enter, and if so, we use the ref object to get the input’s value and update the updated variable with it.

While the data in a controlled input is handled by React state, the data in an uncontrolled input is handled by the DOM itself. This is why the input in the example above doesn’t have a value prop or onChange event handler set. Instead, we access the input field value with a React ref. The DOM updates this value when the text in the input is changed.

We create a ref object with the useRef hook and set it to the ref prop of the input. Doing this sets the current property of the ref object to the DOM object that represents the input element.

useRef returns a mutable ref object that does not change value when a component is updated. Also, modifying the value of this object’s current property does not cause a re-render. This is in contrast to the setState update function returned from useState.

Although the React documentation recommends using controlled components, uncontrolled components offer some advantages. You might prefer them if the form is simple and doesn’t need instant validation, and values only need to be accessed on submission.

How to Open a Link in a New Tab Programmatically in React

To open a link in a new tab programmatically in React, call the window.open() method with the link as an argument, e.g., window.open('https://codingbeautydev.com'). The open() method will open the link in a new browser tab.

In the following example, we programmatically open the link in a new tab at the end of a countdown:

App.js

import { useRef, useEffect, useState } from 'react';

export default function App() {
  const [timeLeft, setTimeLeft] = useState(3);
  const interval = useRef();

  useEffect(() => {
    interval.current = setInterval(() => {
      // Decrease "timeLeft" by 1 every second
      setTimeLeft((prev) => prev - 1);
    }, 1000);

    return () => clearInterval(interval.current);
  }, []);

  useEffect(() => {
    // Open the link in a new tab when the countdown ends
    if (timeLeft === 0) {
      clearInterval(interval.current);

      // 👇 Open link in new tab programmatically
      window.open('https://codingbeautydev.com', '_blank', 'noreferrer');
    }
  }, [timeLeft]);

  return (
    <div>
      The link will open in <b>{timeLeft}</b> second(s)
    </div>
  );
}
The link is opened in a new tab when the countdown ends.
The link is opened in a new tab when the countdown ends.

We use the open() method of the window object to programmatically open a link in a new tab. This method has three optional parameters:

  1. url: The URL of the page to open in a new tab.
  2. target: like the target attribute of the <a> element, this parameter’s value specifies where to open the linked document, i.e., the browsing context. It accepts all the values the target attribute of the <a> element accepts.
  3. windowFeatures: A comma-separated list of feature options for the window. noreferrer is one of these options.

Passing _blank to the target parameter makes the link get opened in a new tab.

Being able to open a link in a new tab programmatically means that we can use a button in place of an anchor link to open a URL on click. We’d just set an onClick event listener on the button element and call the window.open() method in the listener.

For example:

App.js

export default function App() {
  const openInNewTab = (url) => {
    window.open(url, '_blank', 'noreferrer');
  };

  return (
    <div>
      <p>Visit codingbeautydev.com in a new tab</p>

      <button
        role="link"
        onClick={() => openInNewTab('https://codingbeautydev.com')}
      >
        Visit
      </button>
    </div>
  );
}
Clicking the button opens the link in a new tab.
Clicking the button opens the link in a new tab.

How to Quickly Open a Link in a New Tab in React

To open a link in a new tab in React, create an anchor (<a>) element and set its target attribute to _blank, e.g., <a href="https://codingbeautydev.com" target="_blank">Link</a>. The _blank value specifies that the link should be opened in a new tab.

For example:

App.js

export default function App() {
  return (
    <div id="app">
      <a href="https://codingbeautydev.com" target="_blank" rel="noreferrer">
        Coding Beauty
      </a>

      <br />
      <br />

      <a
        href="https://codingbeautydev.com/blog"
        target="_blank"
        rel="noreferrer"
      >
        Coding Beauty Blog
      </a>
    </div>
  );
}

The target property of the anchor element specifies where to open the linked document. By default target has a value of _self, which makes the linked page open in the same frame or tab where it was clicked. To make the page open in a new tab, we set target to _blank.

We also set the rel prop to noreferrer for security purposes. It prevents the opened page from gaining any information about the page that it was opened from.

Clicking the link opens the URL in a new tab.
Clicking the link opens it in a new tab.

We can use the window.open() method to programmatically open a link in a new tab in React, e.g., window.open(url, '_blank', 'noreferrer').

For example:

App.js

import { useRef, useEffect, useState } from 'react';

export default function App() {
  const [timeLeft, setTimeLeft] = useState(3);
  const interval = useRef();

  useEffect(() => {
    interval.current = setInterval(() => {
      // Decrease "timeLeft" by 1 every second
      setTimeLeft((prev) => prev - 1);
    }, 1000);

    return () => clearInterval(interval.current);
  }, []);

  useEffect(() => {
    // Open the link in a new tab when the countdown ends
    if (timeLeft === 0) {
      clearInterval(interval.current);

      // 👇 Open link in new tab programmatically
      window.open('https://codingbeautydev.com', '_blank', 'noreferrer');
    }
  }, [timeLeft]);

  return (
    <div>
      The link will open in <b>{timeLeft}</b> second(s)
    </div>
  );
}
The link is opened in a new tab when the countdown ends.
The link is opened in a new tab when the countdown ends.

We use the open() method of the window object to programmatically open a link in a new tab. This method has three optional parameters:

  1. url: The URL of the page to open in a new tab.
  2. target: like the target attribute of the <a> element, this parameter’s value specifies where to open the linked document, i.e., the browsing context. It accepts all the values the target attribute of the <a> element accepts.
  3. windowFeatures: A comma-separated list of feature options for the window. noreferrer is one of these options.

Passing _blank to the target parameter makes the link get opened in a new tab.

Being able to open a link in a new tab programmatically means that we can use a button in place of an anchor link to open a URL on click. We’ll just set an onClick event listener on the button element and call the window.open() method in the listener.

For example:

App.js

export default function App() {
  const openInNewTab = (url) => {
    window.open(url, '_blank', 'noreferrer');
  };

  return (
    <div>
      <p>Visit codingbeautydev.com in a new tab</p>

      <button
        role="link"
        onClick={() => openInNewTab('https://codingbeautydev.com')}
      >
        Visit
      </button>
    </div>
  );
}
Clicking the button opens the link in a new tab.
Clicking the button opens the link in a new tab.

How Does the useDeferredValue Hook Work in React?

React now has concurrency support with the release of version 18. There are numerous features now that help to make better use of system resources and boost app performance. One such feature is the useDefferedValue hook, in this article we’re going to learn about useDeferredValue and understand the scenarios where we can use it.

Why do we need useDefferedValue?

Before we can see this hook in action, we need to understand something about how React manages state and updates the DOM.

Let’s say we have the following code:

App.js

export default function App() {
  const [name, setName] = useState('');

  const computedValue = useMemo(() => {
    return getComputedValue(name);
  }, [name]);

  const handleChange = (event) => {
    setName(event.target.value);
  };

  return (
    <input
      type="text"
      placeholder="Username"
      value={name}
      onChange={handleChange}
    />
  );
}

Here we create a state variable with the useState hook, and a computed value (computedValue) derived from the state. We use the useMemo hook to recalculate the computed value only when the state changes.

So when the value of the input field changes, the name state variable is updated and the computed value is recomputed before the DOM is updated.

This usually isn’t an issue, but sometimes this recalculation process involves a large amount of computation and takes a long time to finish executing. This can reduce performance and degrade the user experience.

For example, we could be developing a feature that lets a user search for an item in a gigantic list:

App.js

function App() {
  const [query, setQuery] = useState('');

  const list = useMemo(() => {
    // 👇 Filtering through large list impacts performance
    return largeList.filter((item) => item.name.includes(query));
  }, [query]);

  const handleChange = (event) => {
    setQuery(event.target.value);
  };

  return (
    <>
      <input type="text" value={query} onChange={handleChange} placeholder="Search"/>
      {list.map((item) => (
        <SearchResultItem key={item.id} item={item} />
      ))}
    </>
  );
}

In this example, we have a query state variable used to filter through a huge list of items. The longer the list, the more time it will take for the filtering to finish and the list variable to be updated for the DOM update to complete.

So when the user types something in the input field, the filtering will cause a delay in the DOM update and it’ll take time for the text in the input to reflect what the user typed immediately. This slow feedback will have a negative effect on how responsive your app feels to your users.

I simulated the slowness in the demo below so you can better understand this problem. There are only a few search results for you to properly visualize it, and they’re each just the uppercase of whatever was typed into the input field.

In this demo, I am typing each character one after the other as quickly as I can, but because of the artificial slowness, it takes about a second for my keystroke to change the input text.

The input doesn't respond to keystrokes fast enough.
The input doesn’t respond to keystrokes fast enough.

useDeferredValue in action

This is a situation where the useDeferredValue hook is handy. useDeferredValue() accepts a state value as an argument and returns a copy of the value that will be deferred, i.e., when the state value is updated, the copy will not update accordingly until after the DOM has been updated to reflect the state change. This ensures that urgent updates happen and are not delayed by less critical, time-consuming ones.

function App() {
  const [query, setQuery] = useState('');

  // 👇 useDefferedValue
  const deferredQuery = useDefferedValue(query);

  const list = useMemo(() => {
    return largeList.filter((item) => item.name.includes(query));
  }, [deferredQuery]);

  const handleChange = (event) => {
    setQuery(event.target.value);
  };

  return (
    <>
      <input type="text" value={query} onChange={handleChange} placeholder="Search" />
      {list.map((item) => (
        <SearchResultItem key={item.id} item={item} />
      ))}
    </>
  );
}

In the example above, our previous code has been modified to use the useDeferredValue hook. As before, the query state variable will be updated when the user types, but this time, useMemo won’t be invoked right away to filter the large list, because now deferredQuery is the dependency useMemo is watching for changes, and useDeferredValue ensures that deferredQuery will not be updated until after query has been updated and the component has been re-rendered.

Since useMemo won’t be called and hold up the DOM update from the change in the query state, the UI will be updated without delay and the input text will change once the user types. This solves the responsiveness issue.

After the query state is updated, then deferredQuery will be updated, causing useMemo to filter through the large list and recompute a value for the list variable, updating the list of items shown below the input field.

The input responds instantly to keystrokes.
The input field responds instantly to keystrokes.

As you can see in the demo, the text changes immediately as I type, but the list lags behind and updates sometime later.

If we keep changing the input field’s text in a short period (e.g., by typing fast), the deferredQuery state will remain unchanged and the list will not be updated. This is because the query state will keep changing before useDeferredValue can be updated, so useDeferredValue will continue to delay the update until it has time to set deferredQuery to the latest value of query and update the list.

Here’s what I mean:

Typing quickly prevents the list from updating right away.
Typing quickly prevents the list from updating right away.

This is quite similar to debouncing, as the list is not updated till a while after input has stopped.

Tip

Sometimes in our apps, we’ll want to perform an expensive action when an event occurs. If this event happens multiple times in a short period, the action will be done as many times, decreasing performance. To solve this, we can set a requirement that the action only be carried out “X” amount of time since the most recent occurrence of the event. This is called debouncing.

For example, in a sign-up form, instead of sending a request once the user types to check for a duplicate username in the database, we can make the request get sent only 500 ms since the user last typed in the username input field (or of course, we could perform this duplicate check after the user submits the form instead of near real-time).

Since the useDeferredValue hook defers updates and causes additional re-render, it’s important that you don’t overuse it as this could actually cause the performance problems we’re trying to avoid, as it forces React to do extra re-renders in your app. Use it only when you have critical updates that should happen as soon as possible without being slowed down by updates of lower priority.

Conclusion

The useDeferred value accepts a state variable and returns a copy of it that will not be updated until the component has been re-rendered from an update of the original state. This improves the performance and responsiveness of the app, as time-consuming updates are put off to a later time to make way for the critical ones that should be shown in the DOM without delay for the user to see.

How to Get the Mouse Position in React

To get the position of the mouse in React, add a mousemove event handler to the window object and access the clientX and clientY properties of the MouseEvent object to get the X and Y coordinates of the mouse respectively.

For example:

App.js

import { useEffect, useState } from 'react';

export default function App() {
  const [mousePos, setMousePos] = useState({});

  useEffect(() => {
    const handleMouseMove = (event) => {
      setMousePos({ x: event.clientX, y: event.clientY });
    };

    window.addEventListener('mousemove', handleMouseMove);

    return () => {
      window.removeEventListener(
        'mousemove',
        handleMouseMove
      );
    };
  }, []);

  return (
    <div>
      The mouse is at position{' '}
      <b>
        ({mousePos.x}, {mousePos.y})
      </b>
    </div>
  );
}
The current mouse position is displayed.
The current mouse position is displayed.

The mousemove event is triggered on an element when the mouse hovers on it. To be more precise, it is fired when the mouse is moved and the hotspot of the cursor is within the bounds of the element.

We attach the event listener to the window object to trigger the event whenever the mouse is moved anywhere in the page.

We passed an empty dependencies array to the useEffect hook because we want to add a handler for the mousemove event only once – when the component mounts.

In the cleanup function, we use the removeEventListener() method to remove the event listener previously added and prevent a memory leak when the component is unmounted.

The mousemove event listener receives a MouseEvent object used to access information and perform actions related to the event. We use the clientX and clientY properties of this object to get the position of the mouse on the X-coordinate and Y-coordinate respectively in the viewport of the application.

Get mouse position relative to element in React

In the previous example we were able to get the mouse position in global coordinates.

In global coordinates position (0,0) is at the top left of the webpage and position (Xmax, Ymax) is at the bottom right.

We might instead want to get the mouse position within the region of an element.

To get the mouse position relative to an element in React, set an onMouseMove event handler on the element, then calculate the local X and Y position using properties of the MouseEvent object passed to the event handler.

For example:

App.js

import { useEffect } from 'react';
import { useState } from 'react';

export default function App() {
  const [globalMousePos, setGlobalMousePos] = useState({});
  const [localMousePos, setLocalMousePos] = useState({});

  const handleMouseMove = (event) => {
    // 👇 Get mouse position relative to element
    const localX = event.clientX - event.target.offsetLeft;
    const localY = event.clientY - event.target.offsetTop;

    setLocalMousePos({ x: localX, y: localY });
  };

  useEffect(() => {
    const handleMouseMove = (event) => {
      setGlobalMousePos({
        x: event.clientX,
        y: event.clientY,
      });
    };

    window.addEventListener('mousemove', handleMouseMove);

    return () => {
      window.removeEventListener(
        'mousemove',
        handleMouseMove
      );
    };
  }, []);

  return (
    <div>
      <div
        style={{
          border: '1px solid gray',
          display: 'inline-block',
          padding: '75px',
          textAlign: 'center',
        }}
        onMouseMove={handleMouseMove}
      >
        Local
        <br />
        <b>
          ({localMousePos.x}, {localMousePos.y})
        </b>
      </div>
      <br />
      Global
      <br />
      <b>
        ({globalMousePos.x}, {globalMousePos.y})
      </b>
    </div>
  );
}

Now the resulting X and Y coordinates will be relative to the element. For example, position (0,0) will be at the top left of the element, not the page:

The mouse position relative to the element is shown.
The mouse position relative to the element is shown.

We subtract the offsetLeft property of the element from the clientX property of the MouseEvent object to get the X position relative to the element.

Similarly, to get the Y position, we subtract the offsetTop property of the element from the clientY property of the MouseEvent object.

const handleMouseMove = (event) => {
  // Calculate position within bounds of element
  const localX = event.clientX - event.target.offsetLeft;
  const localY = event.clientY - event.target.offsetTop;

  setLocalMousePos({ x: localX, y: localY });
};

The offsetLeft property returns the number of pixels between the left position of an element and that of its parent.

Likewise, the offsetTop property returns the number of pixels between the top position of an element and that of its parent.

How to Get the Value of a Form Input on Submit in React

To get the value of form input on submit in React:

  1. Create a state variable to store the value of the input.
  2. Set an onChange event handler on the input to update the state variable when the input’s value changes.
  3. Set an onSubmit event handler on the form element.
  4. Access the value of the input field in the onSubmit event handler.

For example:

App.js

import { useState } from 'react';

export default function App() {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [message, setMessage] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();

    setMessage(`Hello ${firstName} ${lastName}!`);
    setFirstName('');
    setLastName('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        id="firstName"
        name="firstName"
        value={firstName}
        placeholder="First Name"
        onChange={(event) =>
          setFirstName(event.target.value)
        }
      />

      <br />
      <br />

      <input
        type="text"
        id="lastName"
        name="lastName"
        value={lastName}
        placeholder="Last Name"
        onChange={(event) => {
          setLastName(event.target.value);
        }}
      />

      <br />
      <br />

      <button type="submit">Submit</button>

      <br />
      <br />

      <h2>{message}</h2>
    </form>
  );
}
The input values are used to display a message on form submit.

With the useState() hook we create a state variable to store the current value of each input field. We also created another state variable (message) that will be updated with the values of the input fields after the user submits the form.

We set an inline onChange event handler on each input field to make the handler get called whenever the input field changes. In each handler, we use the target property of the Event object the handler receives to access the input element object. The value property of this object contains the input value, so we pass it to the state update function to update the value of that particular input.

The button element in the form has a type of submit, so every time the user clicks it, the submit event is triggered. We set an onSubmit event handler on the form to listen to this event. In this handler, we call preventDefault() on the Event object, to prevent the page from refreshing when the form is submitted.

To get the input values in this event handler after the form is submitted, we simply access the firstName and lastName state variables.

Get input value on form submit with event.target

We can also get the value of a form input on submit using the target property of the Event object. This is useful in cases where we don’t track the input value with a state variable, i.e., an uncontrolled input.

App.js

import { useState } from 'react';

export default function App() {
  const [message, setMessage] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();

    const firstName = event.target.firstName.value;
    const lastName = event.target.lastName.value;
    setMessage(`Hello ${firstName} ${lastName}!`);

    event.target.reset();
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        id="firstName"
        name="firstName"
        placeholder="First Name"
      />

      <br />
      <br />

      <input
        type="text"
        id="lastName"
        name="lastName"
        placeholder="Last Name"
      />

      <br />
      <br />

      <button type="submit">Submit</button>

      <br />
      <br />

      <h2>{message}</h2>
    </form>
  );
}

In an onSubmit event handler, the target property of the Event object returns the form element object (of type HTMLFormElement).

Because we set the name prop on the input element, the form element object will contain a property with the same name that returns the input element object, i.e., after setting name to firstName on an input element, we can access the element object for this input with the firstName property on the form element object.

After getting the value of each input and displaying the message, we call the reset() method on the form element object to restore the default values of the form’s input elements. This is how we clear the text in the input fields in this example since they’re not controlled by state variables.

Get input value on form submit with ref

We can also use a component ref to get the value of an uncontrolled form input on submit in React.

App.js

import { useState, useRef } from 'react';

export default function App() {
  const [message, setMessage] = useState('');
  const firstNameRef = useRef(undefined);
  const lastNameRef = useRef(undefined);

  const handleSubmit = (event) => {
    event.preventDefault();

    const firstName = firstNameRef.current.value;
    const lastName = lastNameRef.current.value;

    setMessage(`Hello ${firstName} ${lastName}!`);

    event.target.reset();
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        id="firstName"
        name="firstName"
        placeholder="First Name"
        ref={firstNameRef}
      />

      <br />
      <br />

      <input
        type="text"
        id="lastName"
        name="lastName"
        placeholder="Last Name"
        ref={lastNameRef}
      />

      <br />
      <br />

      <button type="submit">Submit</button>

      <br />
      <br />

      <h2>{message}</h2>
    </form>
  );
}

The data in a controlled input is handled by React state, but for an uncontrolled input, it is handled by the DOM itself. This is why the input in this example above doesn’t have a value prop or onChange event handler set. Instead, we access the input field’s value with a React ref. The DOM updates this value when the text in the input is changed.

For each input, we create a ref object with the useRef() hook and set it to the ref prop of the input. Doing this sets the current property of the ref object to the DOM object that represents the input element.

useRef() returns a mutable ref object that doesn’t change value when a component is updated. Also, modifying the value of this object’s current property doesn’t cause a re-render. This is in contrast to the setState update function returned from useState().

Although the React documentation recommends using controlled components, uncontrolled components offer some advantages. You might prefer them if the form is very simple and doesn’t need instant validation, and values only need to be accessed when the form is submitted.

How to Get an Input Value On Change in React

To get the value of an input on change in React, set an onChange event handler on the input, then use the target.value property of the Event object passed to the handler to get the input value.

For example:

App.js

import { useState } from 'react';

export default function App() {
  const [message, setMessage] = useState('');

  const handleChange = (event) => {
    // 👇 Get input value from "event"
    setMessage(event.target.value);
  };

  return (
    <div>
      <input
        type="text"
        id="message"
        name="message"
        onChange={handleChange}
      />

      <h2>Message: {message}</h2>
    </div>
  );
}
Getting an input value on change in React.
The message is updated when the input value changes.

Here we create a state variable (message) to track the input value. By setting an onChange event handler, the handler function will get called whenever the text in the input field changes.

The handler function will be called with an Event object containing data and allowing actions related to the event. The target property of this object lets us access the object representing the input element.

This input element object has a value property which returns the text currently in the input field. So we call the setMessage() function with this property as an argument to update the message variable with the input value, and this reflects on the page after the DOM is updated.