React has solidified its place as a go-to library for building dynamic and responsive user interfaces. With its declarative approach and component-based architecture, React simplifies the complex process of developing modern applications. However, like any powerful tool, there are features and best practices that often fly under the radar, even for experienced developers.
In this blog, we’ll delve into some of these overlooked aspects of React—features that can enhance your development workflow, optimize performance, and help you write cleaner, more efficient code.
1. Layout Hooks (useLayoutEffect)
Everyone has heard about useEffect
hook which lets you effectful code whenever a dependency changes. useLayoutEffect
is a version of useEffect
hook which fires whenever the browser repaints the screen it can be useful in many scenarios.
Tooltips
import { useState, useRef, useLayoutEffect } from 'react';
function Tooltip() {
const ref = useRef(null);
const [tooltipHeight, setTooltipHeight] = useState(0);
useLayoutEffect(() => {
const { height } = ref.current.getBoundingClientRect();
setTooltipHeight(height);
}, []);
// ...
This code here checks before the screen is rendered weather the tooltip would be able to fit the screen or not and therefore rearranges itself.
For detailed explanation visit, https://react.dev/reference/react/useLayoutEffect
2. Outlet (React Router)
Though not a part of react natively, react router is a very famous and useful library when it comes to managing routing in react. With its popularity there are many features mentioned in its documentation which go unnoticed.
How to use outlet
Ever designed a dashboard for your project? where the topbar and sidebar elements are always constant and only a portion of page is changing between different routes? That's exactly where the concept of nested routing and outlet comes into play
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<Outlet />
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Dashboard />}>
<Route path="messages" element={<DashboardMessages />} />
<Route path="tasks" element={<DashboardTasks />} />
</Route>
</Routes>
);
}
The parent route path="/"
signifies the placeholder or the layout of the page where you would want to load the dynamic components, Dashboard()
in the above example would have only a heading and the following content on page would depend on the route we are on /messages
or /tasks
. We can also create a context for all the subpages using outletContext
Read more on: https://reactrouter.com/en/main/components/outlet
3. Loading optimization (React.Suspense
+ Await
)
React with the help of React Router supports loaders and fallbacks without using any other library, here is a simple example on how to use them.
function Book() {
const { book, reviews } = useLoaderData();
return (
<div>
<h1>{book.title}</h1>
<p>{book.description}</p>
<React.Suspense fallback={<ReviewsSkeleton />}>
<Await
resolve={reviews}
errorElement={
<div>Could not load reviews 😬</div>
}
children={(resolvedReviews) => (
<Reviews items={resolvedReviews} />
)}
/>
</React.Suspense>
</div>
);
}
Note:
Await
expects to be rendered inside of a<React.Suspense>
or<React.SuspenseList>
parent to enable the fallback UI.
Summary
We explored some often overlooked yet powerful features of React that can significantly enhance your development process. We started with nested routing and the use of the Outlet
component, which simplifies the handling of child routes within your applications. Next, we delved into layout hooks, particularly useLayoutEffect
, which is crucial for executing updates before the browser repaints, ensuring smoother UI interactions. We also discussed the use of React's Await
and Suspense
tags, which help manage asynchronous operations more effectively, allowing you to build faster and more responsive user interfaces. By understanding and utilizing these features, you can write cleaner, more efficient React code that’s optimized for both performance and scalability.