useLayoutEffect: The savior
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>
);
};
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;
How does it work?
There are two concepts involved here.
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.