feat: 修复动态 reducer 注入导致的运行时错误
This commit is contained in:
10
src/App.js
10
src/App.js
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user