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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 12:15:49