Next.js切换分类后useState值不更新,页面未渲染新帖子问题
问题
我用Next.js开发博客时,在分类展示帖子页面遇到问题:通过getServerSideProps获取对应分类的帖子后存入useState的postsList,点击切换分类的链接后,页面仍显示旧帖子,必须刷新才能拿到最新数据。
组件完整代码如下:
const News: FC<NewsCat> = ({ news, category }) => { const [selectedCateg, setSelectedCateg] = useState(category); //UseState with posts const [postsList, setPostsList] = useState(news); const [page, setPage] = useState(1); const handleClick = (categ: string) => { setSelectedCateg(categ); }; return ( <div className={styles.wrap}> <nav className={styles.categories}> //Category list {list.map((i: string, index) => ( <Link href={`/category/${listEng[index]}`} key={index}> <button className={styles.active} onClick={() => handleClick(listEng[index])} > <h2>{i}</h2> </button> </Link> ))} </nav> <div className={styles.catTitle}> <h1>{newsTranslate(selectedCateg)}</h1> </div> <div className={styles.mainBlock}> {postsList.map((i: News) => ( <div key={i._id} className={styles.normal_card}> <div className={styles.normal_card_img}> <Link href={`/news/${i._id}`}> <img src={i?.image} alt="" /> </Link> <div className={styles.desc}> <div className={styles.up_desc}> <Link href={`/category/${category}`}> <h6>{newsTranslate(i.category)}</h6> </Link> <h6>| {moment(i.createdAt).format("LLL")}</h6> </div> <Link href={`/news/${i._id}`}> <h2 className={styles.titleDesc}> {i.title?.length > 150 ? `${i.title?.substring(0, 90)}...` : i.title} </h2> </Link> </div> </div> <div className={styles.normal_card_desc}> <h4>{moment(i.createdAt).format("LLL")}</h4> </div> </div> ))} </div> <div className={styles.loadMoreButton}> <button onClick={async () => { setPage(page + 1); console.log(page); const getNextPosts = await axios.get( `http://localhost:3000/api/category/${category}`, { params: { page: page, }, } ); setPostsList([...postsList, ...getNextPosts.data]); }} > LoadMore </button> </div> </div> ); }; export default News; export const getServerSideProps: GetServerSideProps = async ({ params, }: any) => { const res = await axios.get( `http://localhost:3000/api/category/${params.category}`, { params: { page: 1 } } ); return { props: { news: res?.data, category: params?.category, }, }; };
我知道用useEffect监听news更新postsList能解决,但觉得不是最优方案,想知道更好的解决办法。
最优解决方案
核心问题是:useState(news)只会在组件首次挂载时初始化postsList,切换分类后getServerSideProps返回新的news props,但组件不会自动更新本地状态。以下是更合理的优化方案:
1. 拆分状态,避免冗余同步
不需要把服务端返回的news完全存入本地状态,而是将news作为初始列表,本地状态只存储加载更多时追加的帖子。这样既利用了服务端渲染的初始数据,又从根源避免了状态不同步问题:
const News: FC<NewsCat> = ({ news, category }) => { const [selectedCateg, setSelectedCateg] = useState(category); // 仅存储加载更多的帖子,初始数据直接用props的news const [morePosts, setMorePosts] = useState<News[]>([]); const [page, setPage] = useState(1); const handleClick = (categ: string) => { setSelectedCateg(categ); }; // 合并初始帖子和加载更多的帖子,直接用于渲染 const postsList = [...news, ...morePosts]; // 其余JSX结构不变,渲染时用postsList即可 };
2. 修复加载更多的闭包陷阱
原加载更多代码存在闭包问题,page和postsList都是旧值,改用函数式更新确保拿到最新状态:
<button onClick={async () => { // 用函数式更新获取最新page值 const nextPage = page + 1; setPage(nextPage); const getNextPosts = await axios.get( `http://localhost:3000/api/category/${category}`, { params: { page: nextPage, }, } ); // 函数式更新morePosts,确保基于最新状态追加 setMorePosts(prevMore => [...prevMore, ...getNextPosts.data]); }} > LoadMore </button>
3. 分类切换时重置分页状态
切换分类后,getServerSideProps会返回新的category和news,此时需要重置分页相关状态,确保新分类的加载从第一页开始:
useEffect(() => { // 当分类变化时,重置加载更多的状态 setMorePosts([]); setPage(1); }, [category]);
这个useEffect是必要逻辑,不属于冗余操作,因为路由切换后组件会复用,必须重置分页状态才能匹配新分类的初始数据。
内容的提问来源于stack exchange,提问作者markiv
相关产品推荐
相关产品推荐

