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
相关产品推荐
相关产品推荐

