React重渲染更新值报ordersToDisplay初始化前访问引用错误
问题根源
你遇到的初始化报错核心是两个问题:
- 变量命名冲突:顶部导入的JSON订单数据、组件内
useMemo声明的筛选后订单变量用了完全相同的名字ordersToDisplay,JS执行到useMemo回调时,会优先读取当前函数作用域内的ordersToDisplay变量,但此时该变量还未完成初始化,触发暂时性死区报错。 - 逻辑错位:你在按钮点击回调里直接取数组长度,此时筛选逻辑还未执行,拿到的永远是全量订单的长度,无法同步筛选后的数量;另外路由配置错误把动态状态拼入静态路由定义,会导致路由匹配异常。
修复方案
按以下步骤调整代码即可:
- 首先重命名导入的原始JSON数据,从根源解决命名冲突
- 修正
useMemo内的筛选逻辑,基于原始数据做过滤 - 新增副作用监听,在筛选后的订单列表变化时,同步更新文档标题和全局状态
- 调整路由配置,路由路径保持静态规则,动态订单数仅用于标题更新(如果确实需要路径带订单数,用动态路由参数写法即可,不要直接把state拼入path定义)
修复后的OrdersPage组件代码
import { Col, Row } from "antd"; import { useContext, useEffect, useMemo, useState } from "react"; import Order from "../components/Order"; import { AppContext } from "../context/Context"; import AntButton from "../elements/Button"; // 重命名原始导入的订单数据,避免和筛选后变量重名 import rawOrders from "../orders.json"; const OrdersPage = () => { const [filteringStatus, setFilteringStatus] = useState(""); const { setOrderAmount } = useContext(AppContext) const setDiplayAcceptedOrders = () => { setFilteringStatus("accepted"); }; const setDiplayCompletedOrders = () => { setFilteringStatus("complete"); }; const setDiplayInProgressOrders = () => { setFilteringStatus("inProgress"); }; // 基于原始数据做筛选,返回最终展示的订单列表 const ordersToDisplay = useMemo(() => { if (filteringStatus) { return rawOrders.filter((i) => i.orderStatus === filteringStatus); } return rawOrders; }, [filteringStatus]); // 监听展示订单列表变化,同步更新文档标题和全局状态 useEffect(() => { const orderNumber = ordersToDisplay.length; // 更新文档标题 document.title = `/${orderNumber}orders`; // 更新全局orderAmount状态 setOrderAmount(orderNumber); }, [ordersToDisplay, setOrderAmount]) return( <div className="container"> <Row justify="space-between" align="middle"> <Col span={6}> <h1>Orders</h1> </Col> <Col span={6}> <AntButton onClick={setDiplayAcceptedOrders} name="Accepted"/> </Col> <Col span={6}> <AntButton onClick={setDiplayInProgressOrders} name="In Progress"/> </Col> <Col span={6}> <AntButton onClick={setDiplayCompletedOrders} name="Complete"/> </Col> </Row> <Row> <Col span={12}> <h3>{filteringStatus == "" ? "All Orders" : filteringStatus}</h3> {ordersToDisplay.map((e) => { return( <Order key={e.id} productName={e.productName} dateOrdered={e.dateOrdered} orderStatus={e.orderStatus} /> ) })} </Col> </Row> </div> ) } export default OrdersPage;
App组件调整说明
你之前把动态state直接拼入路由path的写法是错误的,useRoutes的路由定义只需要写静态匹配规则即可,如果你不需要路径随订单数变化,直接把订单页路径改成固定的/orders即可:
const App = () => { const [orderAmount, setOrderAmount] = useState("") const Routes = useRoutes([ {path: "/", element: <HomePage/>}, // 路由路径保持静态,动态值仅用于标题更新 {path: "/orders", element: <OrdersPage/>} ]) return ( <AppContext.Provider value={{ orderAmount, setOrderAmount }}> <div> {Routes} </div> </AppContext.Provider> ); }; export default App;
如果你确实需要路由地址也同步显示订单数量(比如地址栏显示
/3orders),不要用上面的静态路由写法,应该用路由导航的方式在orderAmount变化时主动跳转,不要直接修改路由定义的path值。
内容的提问来源于stack exchange,提问作者MicSt
相关产品推荐
相关产品推荐

