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

useEffect未捕获bookingSlot更新?Preview Page无日志输出排查

自定义Hook状态跨组件不同步的问题解决

我有三个文件: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没弄对,按这个步骤来:

  1. 创建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;
};
  1. 在顶层组件套Provider:
// App.tsx
import { DateProvider } from './DateContext';

function App() {
  return (
    <DateProvider>
      {/* 这里放你的所有组件,包括Date Component和Preview Page */}
    </DateProvider>
  );
}
  1. 组件里直接用:
    Date Component和Preview Page还是调用useDateController(),但此时拿到的是Context里的共享状态,修改后所有组件都会同步。

这种方式不会冗余,反而能统一管理状态,避免重复逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:42:44