Logo
BlogCategoriesChannels

The "Wrong Way" To Use React

Discover the differences between 'fetch on render' and 'render as you fetch' in React, and learn how to optimize your data fetching strategies.

Theo - t3․ggTheo - t3․ggSeptember 1, 2024

This article was AI-generated based on this episode

What is 'Fetch on Render' in React?

'Fetch on render' in React refers to initiating data fetching within the component's render cycle. This means the component fetches data once it's rendered, causing an extra wait time for displaying the data.

Typical Use Cases

  • Fetching data when a component mounts.
  • Interactive components that need fresh data on every render.

Common Issues

  • Waterfalls: Data dependencies cause sequential fetching, delaying overall data retrieval.
  • Poor Performance: Multiple network requests can bog down the application's speed.
  • Increased Load Time: Fetching on render can lead to longer load times as each fetching step waits for the previous one to complete.

Waterfalls in Detail

When React components depend on each other for data, it often results in a series of API calls. This sequential fetching slows down the overall process. Imagine needing user information before fetching a list of friends. Each step waits for the previous one, creating a "waterfall."

Poor User Experience

Such performance bottlenecks deteriorate user experience. Users have to wait longer, making the application feel sluggish.

For example, using solutions like React Query can help manage these state changes more efficiently, but the root problem of fetch on render remains challenging.

Addressing these issues often involves transitioning to more optimized patterns like 'render as you fetch,' which we'll discuss next.

What is 'Render as You Fetch'?

'Render as You Fetch' is an advanced pattern in React for optimizing data fetching. Unlike 'fetch on render', this approach allows the server to begin data fetching as soon as the page is requested, not when components are rendered.

Key Differences

  • In 'fetch on render', the component fetches data after it is rendered.
  • In 'render as you fetch', fetching starts before rendering, triggered by the server.

Benefits

  • Improved Performance: Fetching data upfront reduces waiting times for users.
  • Parallel Data Loading: Multiple data requests can happen simultaneously, minimizing delays.
  • Better User Experience: Pages load faster, and users see content sooner, creating a smoother experience.

By leveraging 'render as you fetch', applications can significantly enhance both performance and user satisfaction. This pattern aligns with React's evolving architecture, including the adoption of server components and Suspense.

How Does React Handle Data Fetching?

React's approach to data fetching has evolved considerably:

  • Initial Patterns: Early on, developers used simple methods like componentDidMount to fetch data, causing multiple issues such as waterfalls and excessive re-rendering.

  • Suspense: Introduced to handle async data fetching elegantly, React Suspense allows components to wait for data before rendering, preventing intermediate states that degrade user experience.

  • Server Components: These components enable faster initial load times by fetching data on the server. As a result, the client receives a fully hydrated component tree, reducing the initial data-fetching delay.

The shift towards patterns like render as you fetch greatly enhances performance. Each new feature, from Suspense to server components, aims to optimize the interplay between data fetching and component rendering. This ensures that React applications remain fast, responsive, and user-friendly.

Why is 'Fetch on Render' Considered Bad Practice?

'Fetch on render' is generally discouraged due to its significant performance drawbacks. When components render, they trigger data fetching, causing an unnecessary delay.

Performance Pitfalls

  • Sequential Fetching: Fetching data in sequence slows down the process as each step waits for the previous one to complete.
  • Waterfalls: Creates a cascade of network requests, further delaying data fetching.
  • Increased Load Times: The latency in fetching data increases the overall load time of the application.

Real-world Example

Consider a component that first fetches user information and then uses that data to fetch a list of friends. This approach leads to a "waterfall" effect. The initial user data request has to complete before the friends list request can start, delaying the overall data retrieval process.

Poor User Experience

Such delays can make the application feel sluggish. Users have to wait longer to see the content, which can frustrate them.

Alternative Solutions

Using methods like 'render as you fetch' can alleviate these issues. This approach allows data fetching to begin as soon as the page is requested. It reduces waiting times and improves overall performance.

In sum, avoiding 'fetch on render' can lead to a more responsive and user-friendly application.

How to Implement 'Render as You Fetch' in Your React App?

Implementing 'render as you fetch' in your React application can significantly improve performance and user experience. Follow these steps:

  1. Set Up Your Component

    First, create a basic React component. For example:

    function FriendsList() {
      const [data, setData] = React.useState(null);
    
      // Data fetching logic will be added here
      
      if (!data) return <div>Loading...</div>;
    
      return (
        <ul>
          {data.map(friend => (
            <li key={friend.id}>{friend.name}</li>
          ))}
        </ul>
      );
    }
    
  2. Start Data Fetching Early

    To follow the 'render as you fetch' pattern, start fetching data as soon as possible. Use useEffect to trigger data fetching while the component initializes:

    React.useEffect(() => {
      fetch('/api/friends')
        .then(response => response.json())
        .then(data => setData(data));
    }, []);
    
  3. Using Suspense for Concurrent Fetching

    Incorporate React Suspense to manage the loading state seamlessly:

    const FriendsList = React.lazy(() => import('./FriendsList'));
    
    function App() {
      return (
        <React.Suspense fallback={<div>Loading...</div>}>
          <FriendsList />
        </React.Suspense>
      );
    }
    
  4. Leverage Server Components

    If your project supports server components, use them to fetch data on the server, reducing client load times:

    import { serverOnly } from 'your-server-only-package';
    import { db } from 'your-database-setup';
    
    serverOnly();
    
    export default async function ServerFriendsList() {
      const data = await db.query.friends.findMany();
    
      return (
        <ul>
          {data.map(friend => (
            <li key={friend.id}>{friend.name}</li>
          ))}
        </ul>
      );
    }
    

Implementing 'render as you fetch' efficiently reduces load times and optimizes the user experience. For more advanced setups, such as data access layers, check out our detailed guide on implementing a data access layer in React.

What are the Alternatives to 'Fetch on Render'?

Transitioning away from 'fetch on render' can greatly improve your application's performance. Here are some notable alternatives:

  • Remix: Designed to streamline data loading, Remix moves data loaders outside of React components and into routes. This allows data fetching to start before the components actually render, reducing delays.

  • Relay: This GraphQL-based solution composes multiple fragments into a single query. Relay ensures that data fetching happens concurrently, addressing the waterfall issue. It integrates deeply with your component tree to optimize fetch operations.

  • React Query: Emphasizes prefetching data and caching responses. It works well with 'render as you fetch' by enabling components to access fetched data instantly upon rendering, improving loading times.

  • SWC (Server-Written Components): These components handle data fetching on the server. Once the data is ready, the server sends a fully hydrated component tree to the client, significantly reducing initial load times.

By leveraging these modern patterns, you can avoid the pitfalls of 'fetch on render' and enhance both performance and user experience.

What are the Challenges and Solutions for Data Co-location?

Data co-location in React aims to keep state, logic, and view close together. While it enhances component autonomy, it presents several challenges:

  1. Performance Issues:

    • Fetching data within components can cause multiple re-renders.
    • Large trees with deeply nested components lead to performance bottlenecks.
  2. Sequential Data Fetching:

    • Creates waterfalls, where components wait for each other's data.
    • Results in slow load times and poor user experience.

Solutions

Tanner Lindsley highlights several strategies to mitigate these issues:

  1. Prefetching Data:

    • Start data fetching early using useEffect or route-level loaders.
    • This reduces waiting time when a component renders.
  2. Server-Side Components:

    • Move data fetching to the server. This way, the client receives a fully hydrated component tree.
    • Minimizes initial data-fetching delays.
  3. Use of Context API:

    • Manage global state outside of the component tree with Context.
    • Allows relevant components to re-render without affecting the whole tree.

These approaches can balance the benefits of data co-location with necessary performance optimization. Choosing the right strategy depends on the specific needs of your React application.

FAQs

Loading related articles...