You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React多页面图书应用:NYT畅销书渲染异常及请求优化问询

纽约时报畅销书列表加载优化问题

问题背景

我开发了一个基于React的多页面图书应用,计划在首页底部展示纽约时报(NYT)畅销书列表。核心逻辑为:

  1. 调用NYT Books API获取当前畅销书的ISBN列表
  2. 遍历ISBN列表,调用Google Books API获取对应图书数据并存入state

原代码问题现象

  • 初始渲染时,畅销书列表元素无法显示
  • 跳转其他页面再返回首页,列表元素仅短暂显示后消失
  • 切换主题状态后,列表元素正常显示并保持至页面刷新

将getBooksWithIsbn改为async/await函数后解决了显示问题,但请求总耗时约5秒,希望优化该耗时。


相关代码

Context文件(原版本)

import React, { createContext, useState } from "react"
const Context = createContext()

function ContextProvider(props) {
  const [bestSeller, setBestSeller] = useState([])
  const [theme, setTheme] = useState(
    () => JSON.parse(localStorage.getItem("theme")) || "light"
  )

  async function getBestSellerIsbns() {
    try {
      const url = `https://api.nytimes.com/svc/books/v3/lists/current/hardcover-fiction.json?&api-key=${NYT_API_KEY}`
      const response = await fetch(url)
      const data = await response.json()
      const isbns = data.results.books.map(book => book.primary_isbn10)
      getBooksWithIsbn(isbns)

    } catch (error) {
      console.log(error)
    }
  }

  function getBooksWithIsbn(isbnList) {
      let arr = [];
      for (let isbn of isbnList) {
        fetch(`https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}&printType=books`)
          .then((res) => res.json())
          .then((data) => {
            arr.push(data.items[0])
          })
      }
      setBestSeller(arr)
  }

  function changeTheme() {
    setTheme(prevTheme => prevTheme === "light" ? "dark" : "light")
  }

  return (
    <Context.Provider
      value={{
        bestSeller,
        changeTheme,
        getBestSellerIsbns
      }}
    >
      {props.children}
    </Context.Provider>
  )
}

Homepage组件

const HomePage = () => {
  const { bestSeller, getBestSellerIsbns} = useContext(Context)
  
  useEffect(() => {
    getBestSellerIsbns()
  }, [])

  const bestSellerElements = bestSeller.map(best => (
    <BestsellerEl key={best.id} best={best}/>
  ))

  return (
    <div className="homepage-container">
      <div className="bottom-container">
        <div className="container-title">
          <h1>New York Times Bestsellers:</h1>
        </div>
        <div classname="bestseller-div">
          {bestSellerElements}
        </div>
      </div>
    </div>
  )
}
const Navbar = () => {
  const {changeTheme} = useContext(Context)

  return (
    <div className="navbar">
      <div className="nav-elements">
        <label className="switch">
          <input type="checkbox" onClick={changeTheme} />
          <span className="slider"></span>
        </label>
      </div>
    </div>
  );
}

修改后的getBooksWithIsbn函数(解决显示问题但耗时久)

async function getBooksWithIsbn(isbnList) {
    try {
      let arr = []
      for(let isbn of isbnList) {
        const url = `https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}&printType=books`
        const response = await fetch(url)
        const data = await response.json()
        arr.push(data.items[0])
      }
      setBestSeller(arr)
    } catch (error) {
      console.log(error)
    }
  }

优化方案

1. 并行请求替代串行请求

修改后的async/await是串行执行每个请求,总耗时为所有请求时间之和。改用Promise.all并行发起所有请求,总耗时等于最长的单个请求时间,能大幅缩短加载时间。

优化后的代码:

async function getBooksWithIsbn(isbnList) {
  try {
    // 生成所有ISBN对应的请求Promise
    const bookPromises = isbnList.map(isbn => {
      const url = `https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}&printType=books`;
      return fetch(url)
        .then(res => res.json())
        .then(data => data.items?.[0]); // 用可选链避免无匹配图书时报错
    });

    // 并行等待所有请求完成
    const books = await Promise.all(bookPromises);
    // 过滤无效数据(如未找到的图书)
    const validBooks = books.filter(book => book);
    setBestSeller(validBooks);
  } catch (error) {
    console.error('加载畅销书数据失败:', error);
  }
}

2. 缓存请求结果

避免重复请求相同ISBN的数据,可使用useRef实现内存缓存:

// 在ContextProvider中添加缓存ref
const bookCache = useRef({});

async function getBooksWithIsbn(isbnList) {
  try {
    const bookPromises = isbnList.map(isbn => {
      // 优先使用缓存数据
      if (bookCache.current[isbn]) {
        return Promise.resolve(bookCache.current[isbn]);
      }
      const url = `https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}&printType=books`;
      return fetch(url)
        .then(res => res.json())
        .then(data => {
          const book = data.items?.[0];
          // 将结果存入缓存
          bookCache.current[isbn] = book;
          return book;
        });
    });

    const books = await Promise.all(bookPromises);
    const validBooks = books.filter(book => book);
    setBestSeller(validBooks);
  } catch (error) {
    console.error('加载畅销书数据失败:', error);
  }
}

3. 提前预加载数据

将getBestSellerIsbns的调用时机提前到应用初始化时(如App组件挂载),让用户进入首页前就开始加载数据:

// 在App组件中
useEffect(() => {
  getBestSellerIsbns();
}, [getBestSellerIsbns]);

4. 增强错误处理

针对单个请求失败的情况,返回null并后续过滤,避免影响整个列表的展示:

const bookPromises = isbnList.map(isbn => {
  const url = `https://www.googleapis.com/books/v1/volumes?q=isbn:${isbn}&printType=books`;
  return fetch(url)
    .then(res => {
      if (!res.ok) throw new Error(`请求失败: ${res.status}`);
      return res.json();
    })
    .then(data => data.items?.[0])
    .catch(error => {
      console.error(`加载ISBN ${isbn}失败:`, error);
      return null; // 返回null,后续过滤掉
    });
});

内容的提问来源于stack exchange,提问作者devham23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 18:09:21