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

Next.js路由切换时状态未更新问题求助

问题描述

Next.js 应用内有多个动态路由,每个路由页面都包含表格。筛选器更新表格状态的功能正常,但路由切换时,表格会保留之前页面的筛选状态。

试过的无效方案:

  • 给 PricingTable 组件添加 key={router.asPath}
  • 在 getStaticProps 中返回 key 属性:
return {
    props: {
      pageData,
      serverLocations,
      faq,
      key: data?.serverLocations?.data[0]?.attributes.Slug,
    },
    revalidate: 1,
  };
  • 通过 router.events 监听路由变化来重置状态

组件完整代码:

import { useCallback, useEffect, useState } from "react";
import { Button, useTheme } from "@mui/material";
import PricingTable from "./PricingTable";
import FiltersDialog from "./PricingFilters";
import IconFilters from "./icons/IconFilters";
import Spacer from "./dynamicBlocks/Spacer";
import { useRouter } from "next/router";

export default function TableWithFilters({ theData }: any) {
  const theme = useTheme();
  const router = useRouter();
  const [filtersDialogOpen, setFiltersDialogOpen] = useState(false);

  const Columns = Object.entries(theData[0]).map(([property, columnLabel]) => ({
    property,
    columnLabel,
  }));

  const [state, setState] = useState({
    data: theData,
    filters: Columns.slice(3, -2),
    columns: Columns,
    filterObj: Columns.slice(3, -2).reduce(
      (r: any, { property }: any) => ((r[property] = 0), r),
      {}
    ),
  });
  

  const onFilterApply = ({ target: { value, name } }: any) => {
    const newFilterObj = { ...state.filterObj, [name]: value };
    setState({
      ...state,
      filterObj: newFilterObj,
      data: theData.filter((props: any) =>
        Object.entries(newFilterObj).every(([key, val]: any) => {
          return props[key] >= val;
        })
      ),
    });
  };

  const onRouteChange = useCallback(() => {                              
    setState({                                                           
      ...state,                                                          
      data: theData,                                                     
      filterObj: Columns.slice(3, -2).reduce(                            
        (r: any, { property }: any) => ((r[property] = 0), r),           
        {}                                                               
      ),                                                                 
    });                                                                  
  }, [setState]);                                                        
                                                                         
  useEffect(() => {                                                      
    router.events.on("routeChangeComplete", onRouteChange);              
    return () => router.events.off("routeChangeComplete", onRouteChange);
  }, [onRouteChange, router.asPath]);                                    
                                                                         
  console.log("STATEEE", state);
  console.log("DATAAA", theData);

  return (
    <>
      <FiltersDialog
        openFilters={filtersDialogOpen}
        closeFilters={() => setFiltersDialogOpen(false)}
        staticData={theData}
        filterData={state.filterObj}
        filterProperties={state.filters}
        onFilter={onFilterApply}
      />
      <Button
        onClick={() => setFiltersDialogOpen(!filtersDialogOpen)}
        startIcon={
          <IconFilters
            width='24'
            height='24'
            color={theme.palette.primary.main}
          />
        }
      >
        Open Filters
      </Button>
      <Spacer spacerHeight='40px' />
      <PricingTable
        key={router.asPath}
        tableData={state.data}
        tableColumns={state.columns}
        tableHeader={Columns}
      />
    </>
  );
}

解决方案

1. 修复路由监听回调的依赖问题

之前的 onRouteChange 依赖了 state、theData 和 Columns,但这些都没加入 useCallback 的依赖数组,导致回调捕获的是初始渲染的旧值,路由切换时无法正确重置状态。修改如下:

const onRouteChange = useCallback(() => {
  const initialFilterObj = Columns.slice(3, -2).reduce(
    (r: any, { property }: any) => ((r[property] = 0), r),
    {}
  );
  setState({
    data: theData,
    filters: Columns.slice(3, -2),
    columns: Columns,
    filterObj: initialFilterObj,
  });
}, [theData, Columns]); // 补充所有用到的依赖项

2. 监听 theData 变化自动重置状态

每个路由的 theData 是唯一的,直接监听 theData 的变化来重置状态,比路由事件更可靠:

useEffect(() => {
  const initialFilterObj = Columns.slice(3, -2).reduce(
    (r: any, { property }: any) => ((r[property] = 0), r),
    {}
  );
  setState({
    data: theData,
    filters: Columns.slice(3, -2),
    columns: Columns,
    filterObj: initialFilterObj,
  });
}, [theData, Columns]); // 当数据或列配置变化时重置状态

路由切换时组件会接收新的 theData,触发这个副作用自动重置表格状态,不需要再依赖路由事件。

3. 给 TableWithFilters 组件加 key

如果路由切换后组件实例被复用,在使用该组件的父页面中给它加唯一 key:

<TableWithFilters 
  key={router.asPath}
  theData={yourPageData} 
/>

这样 React 会销毁旧组件实例并创建新实例,状态自然会重置为初始值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 13:09:54