This week I delved into an absolute essential for building a web application! Client-side routing and lazy loading. Let's get right into it!
Topics Covered✅
- Client-side routing
- Lazy-loading
- Prop-drilling and its solutions
1. Routing📎
Routing is one of the first things that I'd do if I was in a hackathon and needed to get a project up and running quickly.
React enables us to build single-page applications (SPAs), where navigating between pages doesn’t trigger a full page reload. Instead of reloading the entire site and fetching a new HTML file each time, React uses client-side routing. This means the app dynamically updates the content and changes the URL without requesting a new page from the server.
This approach makes navigation faster and smoother, since the JavaScript bundle is loaded only once, and React handles rendering the appropriate components for each route.
This is how we'd change the route in a very superficial way
import React from "react";
function Home() {
const goToAbout = () => {
// This causes a full page reload instead of client-side routing
window.location.href = "/about";
};
return (
<div>
<h1>Welcome to Home Page</h1>
<button onClick={goToAbout}>Go to About</button>
</div>
);
}
export default Home;
But you'll notice that here too , the page hard reloads. To avoid this, we use a library that helps in routing — 'react-router-dom'. Here is how we'd use that to do the same routing as above
import React from "react";
import { useNavigate } from "react-router-dom";
function Home() {
const navigate = useNavigate();
const goToAbout = () => {
// This changes route without reloading the page
navigate("/about");
};
return (
<div>
<h1>Welcome to Home Page</h1>
<button onClick={goToAbout}>Go to About</button>
</div>
);
}
export default Home;
But there's a little caveat that comes with the useNavigate()hook we use here. It can only be used inside components that are wrapped by a Router. This means that for the useNavigate hook to work, the component must be rendered within a Router context provided by react-router-dom.
This is how our App() function should look for the above to work-
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./Home";
import About from "./About";
function App() {
return (
// useNavigate works only inside components wrapped by a Router
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}
export default App;
This stops the page from hard reloading and instead does client-side routing! This is crucial for building fast web applications.
2. Lazy loading 💤
Another thing we can do to optimise the user's experience is to add lazy-loading to our application. But that is truly useful only if our application has grown large enough.
But below is an example of how to do it.
import React, { Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
// Lazy load the About component
const About = React.lazy(() => import("./About"));
import Home from "./Home";
function App() {
return (
<BrowserRouter>
{/* Suspense shows fallback while About is being loaded */}
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
export default App;
Notice here that we have to use the Suspense API along with lazy loading else React throws us an error. What this API does is that it allows us to add a "fallback" in case the user's network is too slow and the component cannot load in time.
Another benefit for lazy loading is that it reduces the initial bundle size of our application, leading to faster load times and improved performance, especially for users with slower internet connections.
3. Prop Drilling
In one of the earlier blog posts, when we read about various app optimisation techniques and the need to lower our re-renders we studied how we should push down state to the lowest common ancestors of the nodes that need those state variables.
But this could still lead to scenarios where the state variable is being passed down through multiple components that don't need the variables for themselves but still have to pass it down. This leads to code that can look very unappealing and overtime become difficult to manage.
This visually and syntactically bad looking problem is what we call Prop Drilling. Note that prop drilling doesn't have much to do with performance issues as much as it is about the visual and structural complexity it causes.
Below is a small example of prop drilling-
import React from "react";
function App() {
const user = "Nikhil";
return <Parent user={user} />;
}
function Parent({ user }) {
//Parent doesn't need 'user', but passes it down anyway
return <Child user={user} />;
}
function Child({ user }) {
return <GrandChild user={user} />;
}
function GrandChild({ user }) {
return <h2>Hello, {user}!</h2>;
}
export default App;
Observe here how the user prop travels through multiple components (Parent, Child) that don’t actually need it — they just pass it along so GrandChild can use it. This is prop drilling.
The solution -
The solution, as we discussed earlier, is to somehow move all of the state logic outside the components, so that they exist globally. That way, any component that needs it can just call it and use it without it being needed to passed down.
We can do this using two things-
-
The Context API which is built into React but has its limitations like-
- It can lead to unnecessary re-renders if not used carefully.
- It is not a full-fledged state management solution and may not be suitable for very complex state management needs.
- A state management library like RTK(redux tool kit), Recoil(now archived) or some lightweight library built on principles of Recoil, like Jotai or Zustand. These libraries provide more robust solutions for managing global state in larger applications.
We cover both of these topics in the next blog together!
Things I found interesting this week
- Lazy loading was such a cool concept! Seeing the components load one-by-one in the networks tab was really interesting.
- Client-side-routing really tickled a corner of my brain that I did not know wanted to be tickled. Taught me how the URL change in the search bar when we click on different links on a site.
Wrapping up🔄
Another week gone and some more fundamentals mastered. Really interested to do a state-management library next week. If you have any questions or feedback, make sure to comment and let me know!
I'll be back next week with more. Until then, stay consistent!
Top comments (0)