ReactJS - Suspense 组件



在 React 18 中,引入了新功能<Suspense>,它允许我们的组件在渲染之前“等待”任何东西。因此,我们在本教程中介绍了 Suspense 的基础知识。

Suspense 是第一个功能,它使我们能够创建具有更灵敏的用户界面、使用更少浏览器资源的应用程序。它还为开发人员和设计人员提供了一个更加用户友好的 API。

Suspense 强烈地改变了 React 根据我们应用程序组件状态的变化选择在网页上渲染内容的方式。如果我们的 React 应用中的数据(状态数据)发生变化,并且这些更改不需要从服务器重新加载整个页面,React 渲染引擎会更新 UI。

当网页必须根据在搜索框中输入等任何内容刷新列表时,React 过去的运行方式与它与并发渲染的工作方式之间存在显着差异。

以前,如果我们在搜索框中输入一些东西,React 可能会一次更新列表中的许多东西。这可能会导致用户将网页视为缓慢且无响应。它还迫使我们的计算机加班加点,以保持页面的流畅运行。

由于并发渲染,React 现在更强大了。它能够更有效地管理这些更改。因此,当我们在搜索框中输入时,React 可以确保列表以某种方式刷新。

语法


<Suspense fallback={<Loading />}>
	 	<MyComponent />
</Suspense>

道具

  • children - 这是我们想要在网页上显示的内容 - 如文本、图像或我们想要显示的任何其他内容。或者我们可以说这是我们想要展示的主要内容。
  • fallback - 如果我们想要展示的东西(children )还没有准备好,我们可以有一个备用计划。此备份计划称为“fallback.”。它通常是一些简单的东西,例如加载微调器或基本占位符。

如何使用它?

Suspension 是一项新功能,它允许我们的组件在渲染之前“等待”某些内容。它用于数据获取和等待图像、脚本和其他异步操作加载等。

Suspense 不会检测 Effect 或 Event 处理程序中的数据检索。只有启用了 Suspense 的数据源才会激活 <Suspense> 组件。

例子

因此,我们将使用 React 的一些小应用程序看到<Suspense>功能的一些示例和用法。

示例 - 加载占位符

我们可以在我们网站的某个部分周围放置一个特定的框,并告诉浏览器,“如果这个框内的任何内容需要很长时间才能加载,请显示此加载消息。

假设我们想要显示 “Loading...”正在提取照片列表时发出通知。我们可以这样做——


<Suspense fallback={<LoadingMessage />}>
	 	<PhotosComponent />
</Suspense>

因此,通过这种方式,我们可以在浏览器需要很长时间才能加载时显示加载消息,然后在准备就绪时显示真实内容,从而保持我们的网站看起来漂亮和受欢迎。

现在,让我们创建一个简单的应用程序,该应用程序从 API 获取项目列表

首先,我们需要使用 Create React App 来设置我们的 React 项目。在 src 文件夹中,创建两个组件:ItemList.js 和 LoadingMessage.js。在LoadingMessage.js中,我们将定义一个简单的加载消息组件 -


import React from "react";
function LoadingMessage() {
	 	return <div>Loading...</div>;
}
export default LoadingMessage;

在ItemList.js中,我们将创建一个组件,该组件使用 fetch 函数获取并显示项目列表。您还可以使用 Suspense 组件来处理加载 -


import React, { Suspense, useState, useEffect } from "react";

const fetchItems = () =>
new Promise((resolve) => {
	 	setTimeout(() => {
	 	 	 resolve(["Item 1", "Item 2", "Item 3"]);
	 	}, 2000);
});

function ItemList() {
	 	const [items, setItems] = useState(null); 		
	 	useEffect(() => {
	 	 	 const fetchData = async () => {
	 	 	 	 	const data = await fetchItems();
	 	 	 	 	setItems(data);
	 	 	 };
	 	 	 fetchData();
	 	}, []);
	 	
	 	return (
	 	 	 <div>
	 	 	 	 	<h1>Items</h1>
	 	 	 	 	<ul>
	 	 	 	 	 	 {items && items.map((item, index) => (
	 	 	 	 	 	 	 	<li key={index}>{item}</li>
	 	 	 	 	 	 ))}
	 	 	 	 	</ul>
	 	 	 </div>
	 	);
}

export default ItemList;

在我们的 src/App.js 中,我们将使用 Suspense 组件来包装 ItemList 组件并提供回退 -


import React, { Suspense } from "react";
import ItemList from "./ItemList";
import LoadingMessage from "./LoadingMessage";

function App() {
	 	return (
	 	 	 <div className="App">
	 	 	 	 	<h1>My App</h1>
	 	 	 	 	<Suspense fallback={<LoadingMessage />}>
	 	 	 	 	 	 <ItemList />
	 	 	 	 	</Suspense>
	 	 	 </div>
	 	);
}

export default App;

输出

项目

示例 - 渐进式内容显示

当组件挂起时,回退将由最近的父 Suspense 组件显示。我们可以将多个 Suspense 组件嵌套在一起以创建加载模式。随着下一级别内容变得可访问,每个悬念边界的回退将完成。

假设我们正在创建一个包含两个部分的网页:新闻源和天气小部件,每个部分都有自己的加载动画。因此,这就是我们可以使用多个 Suspense 组件来创建加载序列的方法 -


import React, { Suspense } from "react";

function NewsFeedSpinner() {
	 	return <div>Loading News Feed...</div>;
}

function WeatherWidgetSpinner() {
	 	return <div>Loading Weather Widget...</div>;
}

function NewsFeed() {
	 	// Loading news feed data...
	 	return <div>News Feed Content</div>;
}

function WeatherWidget() {
	 	// Loading weather data...
	 	return <div>Weather Widget Content</div>;
}

function App() {
	 	return (
	 	 	 <Suspense fallback={<NewsFeedSpinner />}>
	 	 	 	 	<NewsFeed />
	 	 	 	 	<Suspense fallback={<WeatherWidgetSpinner />}>
	 	 	 	 	 	 <WeatherWidget />
	 	 	 	 	</Suspense>
	 	 	 </Suspense>
	 	);
}

export default App;

输出

新闻提要

示例 - 处理错误和仅限客户端的内容回退

当我们在 React 中使用流式服务器渲染时,如果组件在服务器上生成问题,React 会继续渲染。相反,它会找到最近的 <Suspense> 组件,并在页面上显示其加载微调器。当网站以这种方式加载时,用户将看到一个微调器。

React 尝试在客户端再次渲染相同的组件。如果仍有问题,React 会显示它。但是,如果客户端没有错误,则不会将错误发送给用户,因为内容最终会成功加载。

我们可以使用它来防止某些组件在服务器上呈现。在服务器环境中抛出一个错误,然后将其包裹在<悬念>边界中,以用回退替换HTML -


import React, { Suspense } from "react";

function Loading() {
	 	return <div>Loading Chat...</div>;
}

function Chat() {
	 	if (typeof window === "undefined") {
	 	 	 throw new Error("Chat should only render on the client.");
	 	}
	 	// The Chat component logic goes here... 		
	 	return <div>Chat Component (Client Only)</div>;
}

export default function App() {
	 	return (
	 	 	 <div>
	 	 	 	 	<h1>Chat App</h1>
	 	 	 	 	<Suspense fallback={<Loading />}>
	 	 	 	 	 	 <Chat />
	 	 	 	 	</Suspense>
	 	 	 </div>
	 	);
}

输出

聊天应用程序

在应用程序中,我们使用了 typeof window === “undefined” 来检查组件是否在服务器端执行,如果是,我们抛出一个错误来显示 “Chat” 组件应该只在客户端呈现。

警告

  • 如果我们应用程序中的某些内容加载时间过长并卡住,React 就会忘记它在做什么。当它完成时,React 只是从头开始。
  • 如果我们的应用程序在加载时显示某些内容,但随后卡住了,它将再次显示加载信息,除非我们使用名为“startTransition”或“useDeferredValue”的特定技巧。
  • 当我们的应用程序需要隐藏当前在屏幕上的内容时,因为它再次卡住了,React 会清理一些项目以避免减慢速度。当它完成时,React 将再次显示所有内容并进行更多的清理。