feat: 修复动态 reducer 注入导致的运行时错误

This commit is contained in:
zdl
2025-11-26 13:59:26 +08:00
parent a0b688da80
commit e6ede81c78
2 changed files with 62 additions and 10 deletions

View File

@@ -114,6 +114,16 @@ function AppContent() {
// 初始化 PostHog // 初始化 PostHog
dispatch(posthogSliceModule.initializePostHog()); 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 初始化已触发'); logger.info('App', 'PostHog 模块空闲时加载完成Redux 初始化已触发');
} catch (error) { } catch (error) {
logger.error('App', 'PostHog 加载失败', error); logger.error('App', 'PostHog 加载失败', error);

View File

@@ -12,6 +12,19 @@ import {
} from '../../lib/posthog'; } from '../../lib/posthog';
import { logger } from '../../utils/logger'; import { logger } from '../../utils/logger';
// ⚡ 模块级缓存:存储 reducer 注入前的事件(避免丢失)
let pendingEventsBeforeInjection = [];
/**
* 获取并清空注入前缓存的事件
* 在 App.js 中 reducer 注入后调用
*/
export const flushPendingEventsBeforeInjection = () => {
const events = [...pendingEventsBeforeInjection];
pendingEventsBeforeInjection = [];
return events;
};
// ==================== Initial State ==================== // ==================== Initial State ====================
const initialState = { const initialState = {
@@ -51,7 +64,15 @@ export const initializePostHog = createAsyncThunk(
'posthog/initialize', 'posthog/initialize',
async (_, { getState, rejectWithValue }) => { async (_, { getState, rejectWithValue }) => {
try { 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) { if (!config.apiKey) {
logger.warn('PostHog', '未配置 API Key分析功能将被禁用'); logger.warn('PostHog', '未配置 API Key分析功能将被禁用');
@@ -112,7 +133,20 @@ export const trackEvent = createAsyncThunk(
'posthog/trackEvent', 'posthog/trackEvent',
async ({ eventName, properties = {} }, { getState, rejectWithValue }) => { async ({ eventName, properties = {} }, { getState, rejectWithValue }) => {
try { 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) { if (!isInitialized) {
logger.warn('PostHog', 'PostHog 未初始化,事件将被缓存', { eventName }); logger.warn('PostHog', 'PostHog 未初始化,事件将被缓存', { eventName });
@@ -160,7 +194,14 @@ export const flushCachedEvents = createAsyncThunk(
'posthog/flushCachedEvents', 'posthog/flushCachedEvents',
async (_, { getState, dispatch }) => { async (_, { getState, dispatch }) => {
try { 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) { if (!isInitialized || eventQueue.length === 0) {
return { flushed: 0 }; return { flushed: 0 };
@@ -281,15 +322,16 @@ export const {
// ==================== Selectors ==================== // ==================== Selectors ====================
export const selectPostHog = (state) => state.posthog; // ⚡ 安全的 selectors支持 reducer 未注入的情况)
export const selectIsInitialized = (state) => state.posthog.isInitialized; export const selectPostHog = (state) => state.posthog || initialState;
export const selectUser = (state) => state.posthog.user; export const selectIsInitialized = (state) => state.posthog?.isInitialized ?? false;
export const selectFeatureFlags = (state) => state.posthog.featureFlags; export const selectUser = (state) => state.posthog?.user ?? null;
export const selectEventQueue = (state) => state.posthog.eventQueue; export const selectFeatureFlags = (state) => state.posthog?.featureFlags ?? {};
export const selectStats = (state) => state.posthog.stats; export const selectEventQueue = (state) => state.posthog?.eventQueue ?? [];
export const selectStats = (state) => state.posthog?.stats ?? initialState.stats;
export const selectFeatureFlag = (flagKey) => (state) => { export const selectFeatureFlag = (flagKey) => (state) => {
return state.posthog.featureFlags[flagKey] || posthogGetFeatureFlag(flagKey); return state.posthog?.featureFlags?.[flagKey] || posthogGetFeatureFlag(flagKey);
}; };
export const selectIsOptedOut = () => posthogHasOptedOut(); export const selectIsOptedOut = () => posthogHasOptedOut();