feat: 从 React Context 迁移到 Redux,实现了:
1. ✅ 集中式状态管理 - PostHog 状态与应用状态统一管理 2. ✅ 自动追踪机制 - Middleware 自动拦截 Redux actions 进行追踪 3. ✅ Redux DevTools 支持 - 可视化调试所有 PostHog 事件 4. ✅ 离线事件缓存 - 网络恢复时自动刷新缓存事件 5. ✅ 性能优化 Hooks - 提供轻量级 Hook 避免不必要的重渲染
This commit is contained in:
272
src/hooks/usePostHogRedux.js
Normal file
272
src/hooks/usePostHogRedux.js
Normal file
@@ -0,0 +1,272 @@
|
||||
// src/hooks/usePostHogRedux.js
|
||||
import { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import {
|
||||
trackEvent,
|
||||
identifyUser,
|
||||
resetUser,
|
||||
optIn,
|
||||
optOut,
|
||||
selectPostHog,
|
||||
selectIsInitialized,
|
||||
selectUser,
|
||||
selectFeatureFlags,
|
||||
selectFeatureFlag,
|
||||
selectIsOptedOut,
|
||||
selectStats,
|
||||
flushCachedEvents,
|
||||
} from '../store/slices/posthogSlice';
|
||||
import { trackPageView } from '../lib/posthog';
|
||||
|
||||
/**
|
||||
* PostHog Redux Hook
|
||||
* 提供便捷的 PostHog 功能访问接口
|
||||
*
|
||||
* 用法示例:
|
||||
*
|
||||
* ```jsx
|
||||
* import { usePostHogRedux } from 'hooks/usePostHogRedux';
|
||||
* import { RETENTION_EVENTS } from 'lib/constants';
|
||||
*
|
||||
* function MyComponent() {
|
||||
* const { track, identify, user, isInitialized } = usePostHogRedux();
|
||||
*
|
||||
* const handleClick = () => {
|
||||
* track(RETENTION_EVENTS.NEWS_ARTICLE_CLICKED, {
|
||||
* article_id: '123',
|
||||
* article_title: '标题',
|
||||
* });
|
||||
* };
|
||||
*
|
||||
* if (!isInitialized) {
|
||||
* return <div>正在加载...</div>;
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <button onClick={handleClick}>点击追踪</button>
|
||||
* {user && <p>当前用户: {user.userId}</p>}
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const usePostHogRedux = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Selectors
|
||||
const posthog = useSelector(selectPostHog);
|
||||
const isInitialized = useSelector(selectIsInitialized);
|
||||
const user = useSelector(selectUser);
|
||||
const featureFlags = useSelector(selectFeatureFlags);
|
||||
const stats = useSelector(selectStats);
|
||||
|
||||
// ==================== 追踪事件 ====================
|
||||
|
||||
/**
|
||||
* 追踪自定义事件
|
||||
* @param {string} eventName - 事件名称(建议使用 constants.js 中的常量)
|
||||
* @param {object} properties - 事件属性
|
||||
*/
|
||||
const track = useCallback(
|
||||
(eventName, properties = {}) => {
|
||||
dispatch(trackEvent({ eventName, properties }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
/**
|
||||
* 追踪页面浏览
|
||||
* @param {string} pagePath - 页面路径
|
||||
* @param {object} properties - 页面属性
|
||||
*/
|
||||
const trackPage = useCallback(
|
||||
(pagePath, properties = {}) => {
|
||||
trackPageView(pagePath, properties);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// ==================== 用户管理 ====================
|
||||
|
||||
/**
|
||||
* 识别用户(登录后调用)
|
||||
* @param {string} userId - 用户 ID
|
||||
* @param {object} userProperties - 用户属性
|
||||
*/
|
||||
const identify = useCallback(
|
||||
(userId, userProperties = {}) => {
|
||||
dispatch(identifyUser({ userId, userProperties }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
/**
|
||||
* 重置用户会话(登出时调用)
|
||||
*/
|
||||
const reset = useCallback(() => {
|
||||
dispatch(resetUser());
|
||||
}, [dispatch]);
|
||||
|
||||
// ==================== 隐私控制 ====================
|
||||
|
||||
/**
|
||||
* 用户选择退出追踪
|
||||
*/
|
||||
const optOutTracking = useCallback(() => {
|
||||
dispatch(optOut());
|
||||
}, [dispatch]);
|
||||
|
||||
/**
|
||||
* 用户选择加入追踪
|
||||
*/
|
||||
const optInTracking = useCallback(() => {
|
||||
dispatch(optIn());
|
||||
}, [dispatch]);
|
||||
|
||||
/**
|
||||
* 检查用户是否已退出追踪
|
||||
*/
|
||||
const isOptedOut = selectIsOptedOut();
|
||||
|
||||
// ==================== Feature Flags ====================
|
||||
|
||||
/**
|
||||
* 获取特定 Feature Flag 的值
|
||||
* @param {string} flagKey - Flag 键名
|
||||
* @returns {any} Flag 值
|
||||
*/
|
||||
const getFlag = useCallback(
|
||||
(flagKey) => {
|
||||
return selectFeatureFlag(flagKey)({ posthog });
|
||||
},
|
||||
[posthog]
|
||||
);
|
||||
|
||||
/**
|
||||
* 检查 Feature Flag 是否启用
|
||||
* @param {string} flagKey - Flag 键名
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isEnabled = useCallback(
|
||||
(flagKey) => {
|
||||
const value = getFlag(flagKey);
|
||||
return Boolean(value);
|
||||
},
|
||||
[getFlag]
|
||||
);
|
||||
|
||||
// ==================== 离线事件管理 ====================
|
||||
|
||||
/**
|
||||
* 刷新缓存的离线事件
|
||||
*/
|
||||
const flushEvents = useCallback(() => {
|
||||
dispatch(flushCachedEvents());
|
||||
}, [dispatch]);
|
||||
|
||||
// ==================== 返回接口 ====================
|
||||
|
||||
return {
|
||||
// 状态
|
||||
isInitialized,
|
||||
user,
|
||||
featureFlags,
|
||||
stats,
|
||||
posthog, // 完整的 PostHog 状态
|
||||
|
||||
// 追踪方法
|
||||
track,
|
||||
trackPage,
|
||||
|
||||
// 用户管理
|
||||
identify,
|
||||
reset,
|
||||
|
||||
// 隐私控制
|
||||
optOut: optOutTracking,
|
||||
optIn: optInTracking,
|
||||
isOptedOut,
|
||||
|
||||
// Feature Flags
|
||||
getFlag,
|
||||
isEnabled,
|
||||
|
||||
// 离线事件
|
||||
flushEvents,
|
||||
};
|
||||
};
|
||||
|
||||
// ==================== 便捷 Hooks ====================
|
||||
|
||||
/**
|
||||
* 仅获取追踪功能的 Hook(性能优化)
|
||||
*/
|
||||
export const usePostHogTrack = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const track = useCallback(
|
||||
(eventName, properties = {}) => {
|
||||
dispatch(trackEvent({ eventName, properties }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return { track };
|
||||
};
|
||||
|
||||
/**
|
||||
* 仅获取 Feature Flags 的 Hook(性能优化)
|
||||
*/
|
||||
export const usePostHogFlags = () => {
|
||||
const featureFlags = useSelector(selectFeatureFlags);
|
||||
const posthog = useSelector(selectPostHog);
|
||||
|
||||
const getFlag = useCallback(
|
||||
(flagKey) => {
|
||||
return selectFeatureFlag(flagKey)({ posthog });
|
||||
},
|
||||
[posthog]
|
||||
);
|
||||
|
||||
const isEnabled = useCallback(
|
||||
(flagKey) => {
|
||||
const value = getFlag(flagKey);
|
||||
return Boolean(value);
|
||||
},
|
||||
[getFlag]
|
||||
);
|
||||
|
||||
return {
|
||||
featureFlags,
|
||||
getFlag,
|
||||
isEnabled,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户信息的 Hook(性能优化)
|
||||
*/
|
||||
export const usePostHogUser = () => {
|
||||
const user = useSelector(selectUser);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const identify = useCallback(
|
||||
(userId, userProperties = {}) => {
|
||||
dispatch(identifyUser({ userId, userProperties }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
dispatch(resetUser());
|
||||
}, [dispatch]);
|
||||
|
||||
return {
|
||||
user,
|
||||
identify,
|
||||
reset,
|
||||
};
|
||||
};
|
||||
|
||||
export default usePostHogRedux;
|
||||
Reference in New Issue
Block a user