
在 react 应用中,useeffect hook 是处理副作用(如数据获取、订阅事件等)的核心机制。当我们需要在组件挂载后执行一系列异步操作,尤其是涉及多个并行请求时,如何确保所有请求都完成后才更新 ui 状态,是一个常见的挑战。不恰当的异步处理方式可能导致加载状态提前改变、数据不完整或频繁的组件重新渲染。
考虑一个场景:我们需要从后端 API 获取一系列书籍 ID,然后针对每个 ID 调用第三方 API 获取详细书籍信息,最后在所有书籍信息获取完毕后统一展示。原始代码的实现方式可能导致以下问题:
function ReadingListPage({ userID }) {
const [books, setBooks] = useState([]);
const [isLoading, setIsLoading] = useState(true);
let bookIds = [];
useEffect(() => {
const getBookInfo = async (bookId) => {
const response = await fetch(`https://www.googleapis.com/books/v1/volumes/${bookId}`);
const resJson = await response.json();
// 问题所在:在循环中直接更新状态
setBooks([...books, resJson]);
Promise.resolve(); // 此处 Promise.resolve() 并无实际作用
};
const getBookIds = async () => {
const response = await fetch(`//localhost:3001/user/reading_list/${userID}`);
const resJson = await response.json();
bookIds = resJson;
await Promise.all(
bookIds.map(async (bookId) => {
await getBookInfo(bookId);
})
);
// 问题所在:虽然 Promise.all 等待了,但 setBooks 的行为是异步的且可能基于旧状态
setIsLoading(false);
};
getBookIds();
}, []);
// ... JSX 渲染部分
}上述代码存在的主要问题是:
解决此问题的核心在于:将所有异步数据获取操作的 结果 收集起来,然后在所有操作完成后 一次性 更新状态。Promise.all 是实现这一目标的理想工具。
优化的思路如下:
import React, { useState, useEffect } from 'react';
// 假设 ContentLayout 和 BookDisplay, BookSection 是已定义的组件
// import { ContentLayout, BookDisplay, BookSection } from './components';
function ReadingListPage({ userID }) {
const [books, setBooks] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// 1. getBookInfo 仅负责获取单个书籍数据并返回
const getBookInfo = async (bookId) => {
try {
const response = await fetch(`https://www.googleapis.com/books/v1/volumes/${bookId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const book = await response.json();
return book; // 返回获取到的书籍数据
} catch (error) {
console.error(`Error fetching book ${bookId}:`, error);
return null; // 发生错误时返回 null 或其他指示
}
};
const getBookIds = async () => {
setIsLoading(true); // 确保每次获取数据前都设置为加载状态
try {
// 获取书籍 ID 列表
const response = await fetch(`//localhost:3001/user/reading_list/${userID}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const bookIds = await response.json();
// 2. 使用 Promise.all 并行获取所有书籍详情,并聚合结果
const allBooksData = await Promise.all(
bookIds.map(async (bookId) => getBookInfo(bookId))
);
// 过滤掉可能因错误而返回 null 的项
const validBooks = allBooksData.filter(book => book !== null);
// 3. 一次性更新 books 状态
setBooks(validBooks);
} catch (error) {
console.error("Error in getBookIds process:", error);
setBooks([]); // 错误时清空书籍列表
} finally {
// 4. 在所有操作完成后,最终更新加载状态
setIsLoading(false);
}
};
getBookIds();
// 依赖数组为空,表示只在组件挂载时执行一次
}, [userID]); // 如果 userID 变化需要重新获取,则将其加入依赖数组
return (
{/* 假设 ContentLayout 和 BookDisplay 是已定义的组件 */}
{/* <ContentLayout>
<BookDisplay>
{isLoading ? (
<div>加载中...</div>
) : (
<BookSection name={"Your Reading List"} books={books}></BookSection>
)}
</BookDisplay>
</ContentLayout> */}
<div>
{isLoading ? (
<div>加载中...</div>
) : (
books.length > 0 ? (
<div>
<h3>您的阅读列表</h3>
{books.map((book, index) => (
<div key={book.id || index}>
<h4>{book.volumeInfo?.title}</h4>
<p>{book.volumeInfo?.authors?.join(', ')}</p>
{/* 更多书籍信息 */}
</div>
))}
</div>
) : (
<div>没有找到书籍。</div>
)
)}
</div>
);
}
export default ReadingListPage;代码改进点:
通过遵循这些最佳实践,可以更有效地在 React useEffect 中处理复杂的异步数据流,构建出性能更优、更稳定的组件。
以上就是在 React useEffect 中高效管理多个异步请求并更新状态的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号