自定义Hook管理事件状态后,Hour组件无法同步更新的问题求助
嘿,我仔细看了你的代码,马上就发现问题所在了——每个组件调用useEvent()的时候,都会创建一套完全独立的状态实例!这就导致你在添加事件的组件里更新的事件列表,和Hour组件里读取的事件列表根本不是同一个东西,Hour组件自然不会感知到变化并更新啦😯
问题核心:自定义Hook的特性
自定义Hook的作用是复用逻辑,但它本质上是把组件里的state、effect等逻辑抽出来,每次调用Hook都会重新初始化这些状态。所以你的Hour组件和添加事件的组件各自调用useEvent(),相当于各自维护了一份独立的events数组、dispatchEvent方法,甚至连localStorage的读写都是各自独立的,两者之间完全没有关联。
解决办法:用Context共享状态
既然需要多个组件共享同一套事件状态,我们可以把useEvent里的状态逻辑放到React Context中,让所有需要的组件从Context里获取状态和方法,这样大家用的都是同一个状态实例,更新时自然能同步。
第一步:创建EventContext和Provider
把原来useEvent里的逻辑搬到Context Provider中,这样所有子组件都能共享这个状态:
import { createContext, useContext, useReducer, useEffect, useCallback } from 'react'; import { format } from 'date-fns'; // 假设你用的是date-fns的format方法 // 保留你原来的reducer和初始化函数 function eventsReducer(events, action) { switch (action.type) { case 'add': { return [ ...events, { date: action.date } ]; } case 'delete': { return events.filter((t) => t.date !== action.date); } default: throw new Error('No such action'); } } const initEvents = () => { const eventsFromStorage = localStorage.getItem('savedEvents'); const parsedEvents = eventsFromStorage ? JSON.parse(eventsFromStorage) : []; return parsedEvents; }; // 创建Context const EventContext = createContext(); // 定义Provider组件,包裹需要共享状态的子组件 export const EventProvider = ({ children }) => { const [events, dispatchEvent] = useReducer(eventsReducer, [], initEvents); useEffect(() => { localStorage.setItem('savedEvents', JSON.stringify(events)); }, [events]); const getEventByDate = useCallback((date) => { return events.filter( event => format(new Date(event.date), 'yyyy MM d H') === format(new Date(date), 'yyyy MM d H') ); }, [events]); // 把需要共享的状态和方法放到Context value里 const contextValue = { events, getEventByDate, dispatchEvent, }; return ( <EventContext.Provider value={contextValue}> {children} </EventContext.Provider> ); }; // 重新定义useEvent Hook,用来获取Context export const useEvent = () => { const context = useContext(EventContext); if (!context) { throw new Error('useEvent必须在EventProvider内部使用哦'); } return context; };
第二步:在父组件中包裹EventProvider
找到你的根组件(比如App.js),用EventProvider包裹所有需要使用事件状态的组件:
import { EventProvider } from './EventContext'; function App() { return ( <EventProvider> {/* 这里放你的Hour组件、添加事件的组件,以及其他需要用到事件状态的组件 */} </EventProvider> ); }
第三步:原组件代码无需改动
你的Hour组件和添加事件的组件依然可以像之前一样调用useEvent(),但现在它们获取的是Context中共享的状态:
// Hour组件 const Hour = ({ dayWithHour }) => { const { getEventByDate } = useEvent(); const hasEvent = !!getEventByDate(dayWithHour).length; return <Container hasEvent={hasEvent}></Container>; }; // 添加事件的组件 const AddEventButton = () => { const { dispatchEvent } = useEvent(); const handleClick = () => { const promptData = prompt('Enter event time: YYYY-MM-DD HH:mm:ss'); if (!promptData) return; dispatchEvent({ type: 'add', date: new Date(promptData) }); }; return <button onClick={handleClick}>添加事件</button>; };
这样修改之后,当你添加新事件时,Context里的events会更新,所有使用useEvent()的组件都会感知到变化,Hour组件里的hasEvent也会随之更新,从而触发组件重新渲染啦!
备注:内容来源于stack exchange,提问作者MaybeRainQ

