6
Chapter 6

Prefetching Techniques in React Applications

Chapter 6 delves into advanced prefetching techniques in React applications, using SWR for efficient data fetching. It highlights how to enhance application responsiveness and user experience through strategic data loading and parallel network requests.

In this chapter, you will learn

  • Implementing SWR for optimized data fetching
  • Strategic use of preload for earlier data loading
  • Balancing JavaScript bundle loading with data fetching for enhanced interactivity

  • In the last chapter, we explored how lazy loading and the Suspense API can defer the loading of larger, performance-impacting chunks, thereby enhancing the initial load speed and user experience. This approach, leveraging lazy loading, successfully reduced unnecessary requests and improved overall performance. Yet, as we wrapped up the chapter, a question arose: could we further optimize this?

    Now, in this chapter, our focus shifts to employing the preload technique to accelerate user interactions even more. Our specific goal is to boost performance when users access the UserDetail.

    Preloading in JavaScript is a technique used to instruct the browser to load certain resources early in the page lifecycle. This is particularly useful for resources that are needed soon after the initial page load, but not immediately. By preloading these resources, you can ensure they are available right when needed, thus reducing load times and improving user interactivity. Preloading is often used for images, scripts, stylesheets, and other critical assets that contribute to smoother user experiences. Implementing preload can be a strategic choice in web development to enhance the performance and responsiveness of web applications.

    Introducing SWR

    We're introducing a package named SWR in this chapter, which will streamline our data fetching process and implement a preload feature.

    SWR, standing for Stale-While-Revalidate, is a caching strategy from Vercel, designed for efficient data fetching in React applications. It first delivers stale data from the cache (if available), then revalidates by fetching fresh data in the background. This approach enhances speed and user experience, as users see immediate data, albeit potentially stale. SWR automatically updates the data once fresh content is available and intelligently handles background updating, automatic revalidation, and error retries. It's ideal for data that frequently changes, where brief displays of slightly outdated information are acceptable.

    Implementing SWR for Preloading

    First, let's add swr to our project:

    yarn add swr

    To use swr, we define a fetcher function, which is essentially a wrapper around the native fetch method:

    const fetcher = (...args) => fetch(...args).then(res => res.json())

    Let's see swr in action within the Profile component:

    // src/profile.tsx import useSWR from 'swr' function Profile () { const { data, error, isLoading } = useSWR('/api/user/123', fetcher) if (error) return <div>failed to load</div> if (isLoading) return <div>loading...</div> // render data return <div>hello {data.name}!</div> }

    Here, useSWR fetches and returns user data, handling loading states and errors seamlessly.

    Integrating preload with SWR

    Preloading with SWR is a technique to fetch and cache data before it's actually needed in the UI. This is done to improve the user experience by reducing the loading time when the user eventually requests that data. The idea is to proactively fetch data in the background, leveraging SWR's efficient caching and revalidation strategy.

    Here's a revised approach to implement preloading in a React component with SWR:

    import { preload } from "swr"; function Component({ user }) { const handleMouseEnter = () => { preload(`/api/user/${user.id}/details`, fetcher); }; return ( <div onMouseEnter={handleMouseEnter}> {/* Render the rest of the component */} </div> ); }

    In this implementation, the preload function is used to initiate a data fetch using SWR. It doesn't directly return or use the fetched data but relies on SWR's caching mechanism.

    SWR optimizes data fetching in React with an efficient caching strategy. On the initial data fetch, SWR stores the response in a cache using the fetch URL as the key. For subsequent requests with the same key, SWR first checks this cache. If the cached data is recent (within the deduping interval), it's used immediately, reducing network requests. Meanwhile, SWR revalidates the data in the background to ensure freshness, updating the cache and UI if new data is fetched. This approach leads to faster load times and a seamless user experience, especially for frequently updated data.

    When the Component component detects a mouse entering its area (via onMouseEnter), it calls preload. This proactively fetches and caches the detailed user data for that friend. Later, when the component that actually needs to display this data is rendered, the data will be retrieved from the SWR cache, making it available immediately if it's within the deduping interval.

    This method of preloading with SWR is particularly useful for scenarios where you can predict which data the user will need next, thereby subtly improving the responsiveness and fluidity of the user experience.

    Let's now modify the Friend component:

    src/friend.tsx
    export const Friend = ({ user }: { user: User }) => { const handleMouseEnter = () => { preload(`/user/${user.id}/details`, () => getUserDetail(user.id)); }; return ( <Popover placement="bottom" showArrow offset={10}> <PopoverTrigger> <button onMouseEnter={handleMouseEnter}> <Brief user={user} /> </button> </PopoverTrigger> {/* existing logic */} </Popover> ); };

    In UserDetailCard, we utilize useSWR for fetching user details:

    src/user-detail-card.tsx
    import useSWR from "swr"; import { getUserDetail } from "../api.ts"; export function UserDetailCard({ id }: { id: string }) { const { data: detail, isLoading: loading } = useSWR( `/user/${id}/details`, () => getUserDetail(id) ); //... }

    With this setup, hovering over a Friend triggers a preloading of the network call to /user/<id>/details. Clicking on it renders the detailed information. SWR handles caching and revalidation, enhancing the user experience.

    Request JS bundle and data in parallel
    Request JS bundle and data in parallel

    Visualizing this, we see separate and parallel loading of data and the JavaScript bundle, significantly improving interactivity.

    Request JS bundle and data in parallel
    Request JS bundle and data in parallel

    To recap our progress, let’s review key strategies we've employed to enhance performance:

    • Initially load only essential content by splitting and employing lazy loading.
    • Whenever feasible, execute network requests in parallel.
    • Implement preloading to retrieve data ahead of user interaction with components.
    • For any third-party libraries, consider code splitting into separate bundles and apply lazy loading as needed.

    By applying these methods, we've significantly boosted our application's performance. But so far, our focus has been predominantly on frontend optimizations. What if we broaden our perspective? Can we streamline our application further by shifting more data fetching to the backend? Or is it possible to pre-render content, updating it only as necessary on the frontend?

    These considerations lead us into backend techniques, which will be our focus in upcoming chapters. We'll explore and discuss Server Side Rendering, Static Site Generation, and React Server Components. We'll delve into how and when to use these approaches to ensure our application is performance-optimized before it even reaches the client side. Stay tuned for more insights.

    6

    You have Completed Chapter 6

    This final chapter wraps up our exploration of network patterns in React. We've covered parallel requests, code splitting, and prefetching to improve performance. Up next, we'll explore server-side technologies to further enhance application performance.

    As we conclude this tutorial, we reflect on the key lessons learned and set the stage for exploring server-side technology and its impact on application performance.

    © 2023