窗口Resize时API获取的Books组件重复渲染问题排查
问题分析与解决方案
你的Home组件在窗口resize时的重复渲染和服务端逻辑、请求处理完全无关——请求仅在组件挂载时执行一次(useEffect空依赖数组保证这点)。下面是具体原因和优化方案:
原因拆解
- React默认行为:浏览器窗口尺寸变化时,React会触发全局重渲染检查,你的组件会因响应式样式(如
md:mt-3这类Tailwind类)或父组件重渲染而跟着重新渲染。 - 初始状态更新冗余:当前代码中两次
setState会触发两次初始渲染,但这是初始化阶段的正常现象,和resize无关。 - 组件未做渲染优化:未使用React的渲染优化API,导致任何全局重渲染触发(比如resize)都会让组件重复渲染。
具体修复方案
1. 修正无效错误提示逻辑
你代码中else分支直接写<p>...</p>完全无效,因为这只是返回JSX却没有渲染到页面,改成在return中处理空状态:
import React, { useState, useEffect } from 'react'; import CardGroup from '../components/CardGroup'; import Banner from '../components/Banner'; export default function Home() { const [recommendedBooks, setRecommendedBooks] = useState([]); const [sciFiClassicsBooks, setSciFiClassicsBooks] = useState([]); useEffect(() => { fetch('http://localhost:5000') .then(response => response.json()) .then(data => { const recommendedCategory = data.allBooks.find(category => category.category === 'Recommended'); const sciFiClassicsCategory = data.allBooks.find(category => category.category === 'SciFi Classics'); // 使用可选链简化逻辑,避免冗余判断 setRecommendedBooks(recommendedCategory?.books || []); setSciFiClassicsBooks(sciFiClassicsCategory?.books || []); }) .catch(error => { console.error('Error fetching data:', error.message); }); }, []); return ( <div className="md:mt-3 lg:mt-8"> <Banner /> {recommendedBooks.length > 0 ? ( <CardGroup key="Recommended" title="Recommended" books={recommendedBooks} /> ) : ( <p>Recommended category not found</p> )} {sciFiClassicsBooks.length > 0 ? ( <CardGroup key="SciFi Classics" title="SciFi Classics" books={sciFiClassicsBooks} /> ) : ( <p>Scifi classic category not found</p> )} </div> ); }
2. 阻止不必要的重渲染
用React.memo包裹Home组件,让组件仅在props或状态变化时才重新渲染:
// 修改组件导出部分 export default React.memo(function Home() { // 原组件逻辑不变 });
如果CardGroup组件也存在同类问题,同样用React.memo包裹它:
// CardGroup组件示例 export default React.memo(function CardGroup(props) { // 原组件逻辑不变 });
3. 服务端代码优化(可选)
虽然服务端不是当前问题的根源,但可以优化书籍查询逻辑,避免嵌套循环:
const express = require('express'); const cors = require('cors'); const path = require('path'); const app = express(); const PORT = process.env.PORT || 5000; app.use(cors()); app.use('/data', express.static(path.join(__dirname, '..', 'client', 'src', 'data'))); const allBooks = [/* 原数组内容不变 */]; // 预先构建ID映射表,提升查询效率 const bookIdMap = new Map(); allBooks.forEach(category => { category.books.forEach(book => { bookIdMap.set(book.id, book); }); }); app.get('/', (_, res) => { res.json({ allBooks }); }); app.get('/book/:id', (req, res) => { const bookId = parseInt(req.params.id); const book = bookIdMap.get(bookId); if (book) { res.json({ book }); } else { res.status(404).json({ error: 'Book not found' }); } }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
内容的提问来源于stack exchange,提问作者andrea siano
相关产品推荐
相关产品推荐

