useLayoutEffect: The savior

Udhayaprakasha
3 min readDec 1, 2022

--

Photo by Lautaro Andreani on Unsplash

Hola Reader!
As a React developer, you may have encountered a scenario where you need to display a dropdown when content overflows a container. In this article, we’ll explore two approaches and find an optimized solution to this problem.

Brute-force approach

One approach is to find the number of characters that fit within the container’s width. Here’s an example code snippet:

import React, { useState, useEffect } from 'react';
import DropDown from './DropDown';

const Activity = (props) => {
const { content } = props;
const [canDisplayDropDown, setCanDisplayDropDown] = useState(false);

const setDropDownStatusByWidth = (content) => {
let width = document.getElementsByClassName('container')[0].offsetWidth;
const allowedLimit = width / 6; //I took the default divisor as 6 based on a character's width
if (content.length > allowedLimit) {
setCanDisplayDropDown(true);
}
};

useEffect(() => {
setDropDownStatusByWidth(content);
});

return (
<div className="container">
<div className="details">
<span>
{content}
</span>
</div>
{canDisplayDropDown && <DropDown />}
</div>
);
};
The output of the Brute-force approach

While this approach may work for some cases, it’s not always precise since the font size varies for each character.

CSS Approach

We can improve this by using CSS. By setting white-space: nowrap, text-overflow: ellipsis, and overflow: hidden on the details class, we can display an ellipsis when the content overflows. The remaining problem is to attach the dropdown next to the content if it overflows.

.details {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

useLayoutEffect approach

Another approach is to use useLayoutEffect, a React hook that helps perform DOM measurements and mutations. We can create a ref and add it to the DOM element containing the content. Then, we check if clientWidth is less than scrollWidth. If it is, the content overflows, and we can update our dropdown-display status accordingly. Here's an example code snippet:

Here is the sample code

import React, { useLayoutEffect } from 'react';
import DropDown from './DropDown';

const Activity = (props) => {
const { content } = props;

const ref = React.createRef();
const [canDisplayDropDown, setCanDisplayDropDown] = useState(false);

useLayoutEffect(() => {
if (ref.current.clientWidth < ref.current.scrollWidth) {
setCanDisplayDropDown(true);
}
}, [ref]);

return (
<div className="container">
<div ref={ref} className="details">
<span>
{content}
</span>
</div>
{canDisplayDropDown && <DropDown />}
</div>
);
};

export default Activity;
The output of useLayoutEffect

How does it work?

There are two concepts involved here.

  1. Refs
  2. useLayoutEffect

We are creating a ref and adding it to a DOM element. From the react documentation: React will assign the current property with the DOM element when the component mounts, and assign it back to null when it unmounts. ref updates happen before componentDidMount or componentDidUpdate lifecycle methods.

The useLayoutEffect fires synchronously after the DOM mutation, before the browser, has a chance to paint. We would find the DOM with the content via ref and identify whether clientWidth is lesser than the scrollWidth, this will return true when the content overflows. Then we can update our dropdown-display status accordingly.

This approach is more precise since it takes into account the font size for each character. Plus, using a ref allows us to directly manipulate the DOM element.

Conclusion

In conclusion, displaying a dropdown when content overflows a container can be solved using different approaches. While the brute-force approach and CSS approach may work for some cases, the useLayoutEffect approach is more precise and allows direct manipulation of the DOM element. Keep this in mind the next time you encounter a similar scenario in your React project.

--

--

Udhayaprakasha
Udhayaprakasha

Written by Udhayaprakasha

Software Engineer | Java, Spring, JS, React, React Native | GCP, Vercel | Building secure, scalable apps with seamless UX. Passionate about code & innovation.

No responses yet