Remix Components: Await

Last Updated : 2 Sep, 2024

The Await component in Remix simplifies handling the asynchronous data by managing loading, success, and error states. It enhances performance and user experience in modern React applications.

Avait Component in Remix

The Await component in Remix is a powerful tool used to handle asynchronous operations within your application's components. It allows developers to fetch data or perform other async tasks and render the results dynamically within the UI. This is particularly useful in modern web development, where applications frequently rely on APIs and data fetching to provide dynamic content.

Syntax:

<Await
resolve={dataPromise}
errorElement={<p>Something went wrong!</p>}
>
{(data) => <YourComponent data={data} />}
</Await>

Props:

  • resolve: The promise to be resolved.
  • errorElement: Component to render in case of an error.
  • children: A function that receives the resolved data.

Returns:

The Await component returns the resolved data or an error element if the promise fails.

There are several ways to utilize the Await component in Remix:

Key Concepts

  • Suspense: A React component that blocks rendering until a promise resolves.
  • Await: A component in Remix which uses Suspense under the hood, to provide better control over loading states.
  • Optimistic UI: Technique in which the UI shows some temporary placeholder or assumed data until actual data is fetched.

Key Features

  • Simplified Asynchronous Data Handling: The Await component makes it easier to handle async data directly within your JSX, reducing the need for complex state management and conditional rendering logic.
  • Improved Performance: By deferring data loading until it's needed, the Await component helps improve the performance of your application and reduces the time to first render and enhancing overall user experience.
  • Error Management: Await allows you to catch and display errors, ensuring that your application can handle the failures without breaking UI.
  • Declarative Loading States: It provides a clean and declarative way to handle loading states and allows you to specify exactly what should be displayed while waiting loading data.

Project Setup

Step 1: Create the Project

Initialize a new Remix project named await-remix using the following command:

npx create-remix@latest await-remix

Step 2: Navigate to Your Project Directory

Move into the await-remix directory

cd await-remix

Step 3: Install Dependencies

Ensure the following dependencies are included:

npm install
npm install @remix-run/react @remix-run/node @remix-run/serve @remix-run/router @remix-run/dev react react-dom

Choose a hosting option (e.g., "Remix App Server") and follow the prompts to complete the setup.

Await-Remix_project_Creation
Project Creation

Check package.json: Ensure your package.json scripts and dependencies are correctly set up:


"scripts": {
"dev": "remix dev",
"build": "remix build",
"start": "remix-serve build"
},
"dependencies": {
"@remix-run/react": "^1.10.1",
"@remix-run/node": "^1.10.1",
"@remix-run/serve": "^1.10.1",
"@remix-run/router": "^1.10.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "^1.10.1",
"typescript": "^5.0.0"
}

Folder Structure

Below is the project structure for Await Remix Project

Await_remix_project_structure
Folder Structure

Approach 1: Basic Usage

Handling simple promises and rendering results.

Syntax:

<Await resolve={fetchData()}>
{(data) => <p>{data.title}</p>}
</Await>

Steps:

  • Create a function that returns a promise, e.g., a data fetch.
  • Use the Await component and pass the promise to the resolve prop.
  • Render the resolved data using the child function.

Example: This example uses the Await component in Remix to handle asynchronous data fetching, displaying a list of photo titles once the data resolves successfully.

JavaScript
// app/routes/_index.tsx

import { Await } from "@remix-run/react";
import { json, LoaderFunction, useLoaderData } from "@remix-run/node";

export let loader: LoaderFunction = async () => {
	const res = await fetch("https://jsonplaceholder.typicode.com/photos");
	const data = await res.json();
	return json(data.slice(0, 10));
};

export default function Index() {
	const data = useLoaderData();

	return (
		<div>
			<h1>Photo List</h1>
			<Await resolve={data}>
				{(photos) =>
					photos.map((photo) => <p key={photo.id}>{photo.title}</p>)
				}
			</Await>
		</div>
	);
}
JavaScript
// app/components/DataDisplay.tsx

import React from "react";

type DataDisplayProps = {
	data: { id: number, title: string }[]
};

const DataDisplay: React.FC<DataDisplayProps> = ({ data }) => {
	return (
		<ul>
			{data.map((item) => (
				<li key={item.id}>{item.title}</li>
			))}
		</ul>
	);
};

export default DataDisplay;

Output:

Render a list of photo titles fetched from the API.

Await_1_output

Approach 2: Error Handling

Providing custom error UI when promises fail.

Syntax:

<Await
resolve={fetchData()}
errorElement={<p>Could not fetch data</p>}
>
{(data) => <p>{data.title}</p>}
</Await>

Steps:

  • Add an errorElement prop to handle failed promises.
  • Test by passing an incorrect API URL.

Example: This example uses the Await component in Remix to handle asynchronous data fetching with error management, displaying either data or an error message based on the fetch operation's outcome.

JavaScript
// app/routes/_index.tsx

import { Await } from "@remix-run/react";
import { json, LoaderFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import DataDisplay from "~/components/DataDisplay";

// Loader function with a deliberate error (wrong URL)
export let loader: LoaderFunction = async () => {
	try {
		const res = await fetch("https://invalid-url"); // Incorrect URL to simulate an error
		if (!res.ok) throw new Error("Network response was not ok");
		const data = await res.json();
		return json(data.slice(0, 10));
	} catch (error) {
		throw new Response("Failed to fetch data", { status: 500 });
	}
};

export default function Index() {
	const data = useLoaderData();

	return (
		<div>
			<h1>Photo List with Error Handling</h1>
			<Await
				resolve={data}
				errorElement={
					<p>Could not fetch data. Please try again later.</p>
				}
			>
				{(photos) => <DataDisplay data={photos} />}
			</Await>
		</div>
	);
}

Output:

Displays "Could not fetch data" if the promise fails.

Await_2_output

Approach 3: Nested Await Components

Handling multiple asynchronous operations within nested components.

Syntax:

<Await resolve={firstPromise()}>
{(data1) => (
<Await resolve={secondPromise()}>
{(data2) => <p>{data1.title} and {data2.title}</p>}
</Await>
)}
</Await>

Steps:

  • Nest Await components to handle multiple async operations.
  • Pass data from each promise to subsequent Await components

Example:This example uses nested Await components in Remix to concurrently fetch and display photos and posts, managing multiple asynchronous operations efficiently in a single component.

JavaScript
// app/routes/_index.tsx

import { Await } from "@remix-run/react";
import { json, LoaderFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import DataDisplay from "~/components/DataDisplay";

// Loader function to fetch two sets of data
export let loader: LoaderFunction = async () => {
	const fetchPhotos = fetch("https://jsonplaceholder.typicode.com/photos");
	const fetchPosts = fetch("https://jsonplaceholder.typicode.com/posts");

	const [photosRes, postsRes] = await Promise.all([fetchPhotos, fetchPosts]);
	const photos = await photosRes.json();
	const posts = await postsRes.json();

	return json({ photos: photos.slice(0, 5), posts: posts.slice(0, 5) });
};

export default function Index() {
	const { photos, posts } = useLoaderData();

	return (
		<div>
			<h1>Nested Await Example</h1>
			<Await resolve={photos}>
				{(photoData) => (
					<Await resolve={posts}>
						{(postData) => (
							<div>
								<h2>Photos</h2>
								<DataDisplay data={photoData} />
								<h2>Posts</h2>
								<ul>
									{postData.map((post) => (
										<li key={post.id}>{post.title}</li>
									))}
								</ul>
							</div>
						)}
					</Await>
				)}
			</Await>
		</div>
	);
}

Output:

Displays combined data from two promises.

Await_3_output

Step to Run the project:

To run the project use below command

npm run dev

Conclusion

The Await component is your go-to tool inside Remix for dealing with asynchronous operations within your UI. Alone or combined with Suspense, it enhances the user experience with clear and responsive feedback during data fetching.

Comment