diff --git a/src/App.js b/src/App.js index 69f71e41..a0641ea6 100755 --- a/src/App.js +++ b/src/App.js @@ -114,6 +114,16 @@ function AppContent() { // 初始化 PostHog dispatch(posthogSliceModule.initializePostHog()); + + // ⚡ 刷新注入前缓存的事件(避免丢失) + const pendingEvents = posthogSliceModule.flushPendingEventsBeforeInjection(); + if (pendingEvents.length > 0) { + logger.info('App', `刷新 ${pendingEvents.length} 个注入前缓存的事件`); + pendingEvents.forEach(({ eventName, properties }) => { + posthogModule.trackEventAsync(eventName, properties); + }); + } + logger.info('App', 'PostHog 模块空闲时加载完成,Redux 初始化已触发'); } catch (error) { logger.error('App', 'PostHog 加载失败', error); diff --git a/src/store/slices/posthogSlice.js b/src/store/slices/posthogSlice.js index 8b5a4287..66216c20 100644 --- a/src/store/slices/posthogSlice.js +++ b/src/store/slices/posthogSlice.js @@ -12,6 +12,19 @@ import { } from '../../lib/posthog'; import { logger } from '../../utils/logger'; +// ⚡ 模块级缓存:存储 reducer 注入前的事件(避免丢失) +let pendingEventsBeforeInjection = []; + +/** + * 获取并清空注入前缓存的事件 + * 在 App.js 中 reducer 注入后调用 + */ +export const flushPendingEventsBeforeInjection = () => { + const events = [...pendingEventsBeforeInjection]; + pendingEventsBeforeInjection = []; + return events; +}; + // ==================== Initial State ==================== const initialState = { @@ -51,7 +64,15 @@ export const initializePostHog = createAsyncThunk( 'posthog/initialize', async (_, { getState, rejectWithValue }) => { try { - const { config } = getState().posthog; + const posthogState = getState().posthog; + + // ⚡ 防御性检查:reducer 尚未注入 + if (!posthogState) { + logger.warn('PostHog', 'PostHog reducer 尚未注入,跳过初始化'); + return { isInitialized: false, skipped: true }; + } + + const { config } = posthogState; if (!config.apiKey) { logger.warn('PostHog', '未配置 API Key,分析功能将被禁用'); @@ -112,7 +133,20 @@ export const trackEvent = createAsyncThunk( 'posthog/trackEvent', async ({ eventName, properties = {} }, { getState, rejectWithValue }) => { try { - const { isInitialized } = getState().posthog; + const posthogState = getState().posthog; + + // ⚡ reducer 尚未注入:缓存到模块级队列(不丢弃) + if (!posthogState) { + logger.debug('PostHog', 'PostHog reducer 尚未注入,事件已缓存', { eventName }); + pendingEventsBeforeInjection.push({ + eventName, + properties, + timestamp: new Date().toISOString() + }); + return { eventName, properties, pendingInjection: true }; + } + + const { isInitialized } = posthogState; if (!isInitialized) { logger.warn('PostHog', 'PostHog 未初始化,事件将被缓存', { eventName }); @@ -160,7 +194,14 @@ export const flushCachedEvents = createAsyncThunk( 'posthog/flushCachedEvents', async (_, { getState, dispatch }) => { try { - const { eventQueue, isInitialized } = getState().posthog; + const posthogState = getState().posthog; + + // ⚡ 防御性检查:reducer 尚未注入 + if (!posthogState) { + return { flushed: 0, skipped: true }; + } + + const { eventQueue, isInitialized } = posthogState; if (!isInitialized || eventQueue.length === 0) { return { flushed: 0 }; @@ -281,15 +322,16 @@ export const { // ==================== Selectors ==================== -export const selectPostHog = (state) => state.posthog; -export const selectIsInitialized = (state) => state.posthog.isInitialized; -export const selectUser = (state) => state.posthog.user; -export const selectFeatureFlags = (state) => state.posthog.featureFlags; -export const selectEventQueue = (state) => state.posthog.eventQueue; -export const selectStats = (state) => state.posthog.stats; +// ⚡ 安全的 selectors(支持 reducer 未注入的情况) +export const selectPostHog = (state) => state.posthog || initialState; +export const selectIsInitialized = (state) => state.posthog?.isInitialized ?? false; +export const selectUser = (state) => state.posthog?.user ?? null; +export const selectFeatureFlags = (state) => state.posthog?.featureFlags ?? {}; +export const selectEventQueue = (state) => state.posthog?.eventQueue ?? []; +export const selectStats = (state) => state.posthog?.stats ?? initialState.stats; export const selectFeatureFlag = (flagKey) => (state) => { - return state.posthog.featureFlags[flagKey] || posthogGetFeatureFlag(flagKey); + return state.posthog?.featureFlags?.[flagKey] || posthogGetFeatureFlag(flagKey); }; export const selectIsOptedOut = () => posthogHasOptedOut();