Next.js SSG页面每次渲染获新props对象,如何避免重复渲染?
问题原因
在Next.js客户端导航场景下(比如通过Link组件切换页面),SSG页面的静态数据会从内置JSON文件中重新解析——即便数组内容完全一致,每次解析都会生成新引用的数组。useMemo依赖判断基于引用相等性(浅比较),因此每次返回Home页面时,allPostsData的引用变化会触发useMemo重新执行,导致打印useMemo log。
解决方法
方法一:用React.memo包裹组件并自定义props比较逻辑
通过React.memo缓存Home组件,自定义比较函数忽略引用变化,只判断数组内容是否一致:
import React from 'react'; // ... 其他导入代码 const Home = ({ allPostsData }) => { // ... 组件原有逻辑 } // 自定义props比较函数,判断帖子数据内容是否一致 const arePropsEqual = (prevProps, nextProps) => { const prevData = prevProps.allPostsData; const nextData = nextProps.allPostsData; if (prevData.length !== nextData.length) return false; return prevData.every((prevItem, index) => { const nextItem = nextData[index]; return prevItem.id === nextItem.id && prevItem.title === nextItem.title && prevItem.date === nextItem.date; }); }; export default React.memo(Home, arePropsEqual); // ... getStaticProps逻辑不变
方法二:抽离列表渲染为独立的memoized组件
把帖子列表的渲染逻辑拆成单独组件,用React.memo包裹,避免父组件重新渲染时影响子组件:
import React from 'react'; // ... 其他导入代码 // 抽离的帖子列表组件,用React.memo包裹 const PostList = React.memo(({ postsData }) => { return postsData.map(({ id, date, title }) => ( <li className={utilStyles.listItem} key={id}> {title} <br /> {id} <br /> {date} </li> )); }); const Home = ({ allPostsData }) => { return ( <Layout home> <ul className={utilStyles.list}> <PostList postsData={allPostsData} /> </ul> </Layout> ) } // ... getStaticProps逻辑不变
如果需要更严格的缓存,也可以给PostList添加和方法一一致的自定义比较函数。
方法三:在组件内用useRef缓存数据内容
通过useRef保存上一次的数据和生成的列表,只有当内容变化时才重新渲染:
import { useMemo, useRef } from 'react'; // ... 其他导入代码 const Home = ({ allPostsData }) => { const cachedData = useRef({ data: allPostsData, posts: null }); const posts = useMemo(() => { // 对比数据内容是否一致 const isSame = cachedData.current.data.length === allPostsData.length && cachedData.current.data.every((item, index) => { const newItem = allPostsData[index]; return item.id === newItem.id && item.title === newItem.title && item.date === newItem.date; }); if (isSame && cachedData.current.posts) { return cachedData.current.posts; } console.log('useMemo log'); const newPosts = allPostsData.map(({ id, date, title }) => ( <li className={utilStyles.listItem} key={id}> {title} <br /> {id} <br /> {date} </li> )); // 更新缓存 cachedData.current = { data: allPostsData, posts: newPosts }; return newPosts; }, [allPostsData]); return ( <Layout home> <ul className={utilStyles.list}> {posts} </ul> </Layout> ) } // ... getStaticProps逻辑不变
内容的提问来源于stack exchange,提问作者Dmitrii Ajtyan
相关产品推荐
相关产品推荐

