useEffect未捕获bookingSlot更新?Preview Page无日志输出排查
我有三个文件:Date Component、Preview Page(承载日期组件)、useDateController(处理日期逻辑的自定义Hook)。三个文件里都加了这段代码:useEffect(() => { console.log(bookingSlot); }, [bookingSlot]);
结果只有Date Component和useDateController里的useEffect能打印出bookingSlot,Preview Page里的useEffect完全没输出;点Preview Page的按钮执行test函数,打印的bookingSlot是空对象。明明在Date Component里改了状态,Preview Page就是拿不到更新。试过把状态提去Context里,但问题没解决,还不想因为提状态搞出冗余代码。
相关代码如下:
useDateController:
export const useDateController = () => { const [bookingSlot, setBookingSlot] = useState<any>({}); // 其他代码... return { bookingSlot, setBookingSlot}; }
Date Component:
const { bookingSlot, setBookingSlot } = useDateController(); // 其他代码... const handleCalendarChange = (date: Dayjs) => { setBookingSlot({ dayNo: dayjs(date).add(1, 'day').day(), date: dayjs(date).format('YYYY-MM-DD'), }); }; const handlePickupTimeChange = (time: string) => { setBookingSlot((oldVal: any) => ({ ...oldVal, pickup_time: pickupTimeObj.time, })); };
Preview Page:
const FilterView = (props) => { const { setUserHasSelectedADate } = useStore(); const { bookingSlot, updateDateRange } = useDateController(); useEffect(() => { console.log(bookingSlot) }, [bookingSlot]); const test = () => { console.log(bookingSlot, 'triggered') } }
问题原因
核心问题很简单:每次组件调用useDateController,Hook内部的useState都会创建一个完全独立的状态实例。Date Component和Preview Page各自拿着自己的bookingSlot,两者根本不是同一个东西。Date Component改的是自己那份状态,Preview Page的状态还是初始的空对象,自然同步不了。
你之前用Context没解决,大概率是用法错了——要么是每个组件都重新创建了Context,要么是没在顶层套Provider,或者组件没正确订阅Context的更新。
解决方案
方案1:让自定义Hook共享单一状态(无冗余)
不用动Context,直接在Hook外部维护全局状态,再用订阅机制让组件同步更新:
修改useDateController:
// 全局存共享状态和订阅者列表 let sharedBookingSlot = {}; const subscribers = new Set(); // 更新全局状态并通知所有订阅组件 const updateSharedState = (newState) => { sharedBookingSlot = newState; subscribers.forEach(callback => callback(sharedBookingSlot)); }; export const useDateController = () => { // 组件本地状态,用来触发重渲染 const [bookingSlot, setLocalBookingSlot] = useState(sharedBookingSlot); useEffect(() => { // 订阅全局状态更新 const callback = (newState) => setLocalBookingSlot(newState); subscribers.add(callback); // 组件卸载时取消订阅 return () => subscribers.delete(callback); }, []); const setBookingSlot = (newState) => { // 支持函数式更新 const finalState = typeof newState === 'function' ? newState(sharedBookingSlot) : newState; updateSharedState(finalState); }; return { bookingSlot, setBookingSlot }; };
现在所有调用这个Hook的组件都会共享同一个sharedBookingSlot,Date Component改状态时,Preview Page会自动同步更新。
方案2:正确用Context(避免冗余)
如果之前Context没弄对,按这个步骤来:
- 创建DateContext和Provider:
// DateContext.tsx import { createContext, useContext, useState } from 'react'; const DateContext = createContext(undefined); export const DateProvider = ({ children }) => { const [bookingSlot, setBookingSlot] = useState({}); return ( <DateContext.Provider value={{ bookingSlot, setBookingSlot }}> {children} </DateContext.Provider> ); }; // 封装成自定义Hook,方便组件调用 export const useDateController = () => { const context = useContext(DateContext); if (!context) { throw new Error('useDateController必须在DateProvider内部使用'); } return context; };
- 在顶层组件套Provider:
// App.tsx import { DateProvider } from './DateContext'; function App() { return ( <DateProvider> {/* 这里放你的所有组件,包括Date Component和Preview Page */} </DateProvider> ); }
- 组件里直接用:
Date Component和Preview Page还是调用useDateController(),但此时拿到的是Context里的共享状态,修改后所有组件都会同步。
这种方式不会冗余,反而能统一管理状态,避免重复逻辑。
内容的提问来源于stack exchange,提问作者Reece Birchmore

