React中setTimeout读取newOrder.number报Cannot read property错误如何解决
错误原因
- 你在组件顶层直接定义的
setTimeout会在组件首次渲染后立即执行,此时createOrder的异步请求还未完成,state.orderCreate.newOrder是初始的undefined,直接访问undefined.number就会抛出对应报错。 useEffect内部的setTimeout存在闭包陷阱:当前渲染周期的useEffect捕获的是本次渲染时的newOrder值,哪怕2秒后接口返回数据更新了newOrder,定时器回调里拿到的还是触发useEffect时的初始空值。- 所有访问
newOrder.number的位置都没有做空值校验,只要newOrder为空就会触发报错。
修复方案
- 删掉组件顶层的
setTimeout,该位置的定时器执行时机完全不符合你的业务需求。 - 新增单独的
useEffect监听newOrder的状态变化,只有当newOrder存在有效值时再触发打印逻辑,不要用硬编码2秒延迟等待接口返回,这种写法受网络波动影响非常不可靠。 - 所有访问
newOrder属性的位置增加可选链操作符?.做兜底,避免空值报错。 - 定时器要添加清理逻辑,避免组件卸载后定时器回调执行引发额外报错。
修复后完整代码
const styles = useStyles(); const { state, dispatch } = useContext(Store); const { order } = state; const { loading, error, newOrder } = state.orderCreate; const print = usePrinter("192.168.1.254", "8008"); // 创建订单逻辑 useEffect(() => { if (order.orderItems.length > 0) { createOrder(dispatch, order); } }, [order, dispatch]); // 监听newOrder变化自动触发打印 useEffect(() => { // 仅当newOrder有有效值时执行逻辑 if (newOrder?.number) { // 如果不需要延迟可以直接删除setTimeout,直接调用print即可 const printTimer = setTimeout(() => { console.log(newOrder.number); print(newOrder.number); }, 2000); // 组件卸载或依赖变化时清理定时器 return () => clearTimeout(printTimer); } }, [newOrder, print]); return ( <Box className={[styles.root, styles.navy]}> <Box className={[styles.main, styles.center]}> <Box> {loading ? ( <CircularProgress /> ) : error ? ( <Alert severity="error">{error}</Alert> ) : ( <> <Typography> Your order number is {newOrder?.number} </Typography> </> )} </Box> </Box> </Box> );
内容的提问来源于stack exchange,提问作者Fynn
相关产品推荐
相关产品推荐

