9dca03f682
## Summary Enables exhaustive React Hook dependency checking and resolves the existing violations across the dashboard and React hooks package. Effects and callbacks now track current values without introducing request, subscription, or render loops. ## Design Dependencies are included directly when the hook lifecycle should follow them. Timers, Remix fetchers, and realtime subscriptions use stable callbacks or latest-value refs where restarting work would change behavior. Unnecessary memoization was removed where ordinary derivation is clearer. Full lint and typechecks for the webapp and React hooks package pass.
93 lines
2.9 KiB
TypeScript
93 lines
2.9 KiB
TypeScript
import { useLocation } from "@remix-run/react";
|
|
import posthog from "posthog-js";
|
|
import { useCallback, useEffect, useRef } from "react";
|
|
import { useOrganizationChanged } from "./useOrganizations";
|
|
import { useOptionalUser, useUserChanged } from "./useUser";
|
|
import { useProjectChanged } from "./useProject";
|
|
|
|
export const usePostHog = (
|
|
apiKey?: string,
|
|
uiHost?: string,
|
|
logging = false,
|
|
debug = false
|
|
): void => {
|
|
const postHogInitialized = useRef(false);
|
|
const location = useLocation();
|
|
const user = useOptionalUser();
|
|
|
|
//start PostHog once
|
|
useEffect(() => {
|
|
if (apiKey === undefined || apiKey === "") return;
|
|
if (postHogInitialized.current === true) return;
|
|
if (logging) console.log("Initializing PostHog");
|
|
posthog.init(apiKey, {
|
|
// Same-origin first-party proxy (see app/routes/ph.$.ts) that forwards to
|
|
// PostHog Cloud EU server-side.
|
|
api_host: "/ph",
|
|
// Point the toolbar at the real PostHog UI; without it, it falls back to /ph.
|
|
ui_host: uiHost,
|
|
cross_subdomain_cookie: true,
|
|
opt_in_site_apps: true,
|
|
debug,
|
|
loaded: function (posthog) {
|
|
if (logging) console.log("PostHog loaded");
|
|
if (user !== undefined) {
|
|
if (logging) console.log("Loaded: Identifying user", user);
|
|
posthog.identify(user.id, { email: user.email });
|
|
}
|
|
},
|
|
});
|
|
postHogInitialized.current = true;
|
|
}, [apiKey, uiHost, logging, debug, user]);
|
|
|
|
useUserChanged((user) => {
|
|
if (postHogInitialized.current === false) return;
|
|
if (logging) console.log("User changed");
|
|
if (user) {
|
|
if (logging) console.log("Identifying user", user);
|
|
posthog.identify(user.id, { email: user.email });
|
|
} else {
|
|
if (logging) console.log("Resetting user");
|
|
posthog.reset();
|
|
}
|
|
});
|
|
|
|
useOrganizationChanged((org) => {
|
|
if (postHogInitialized.current === false) return;
|
|
if (org) {
|
|
if (logging) console.log(`Grouping by organization`, org);
|
|
posthog.group("organization", org.id);
|
|
} else {
|
|
//reset the groups when you go to one of the top-level pages
|
|
if (logging) console.log("Resetting groups");
|
|
posthog.resetGroups();
|
|
}
|
|
});
|
|
|
|
useProjectChanged((project) => {
|
|
if (postHogInitialized.current === false) return;
|
|
if (project) {
|
|
if (logging) console.log(`Grouping by project`, project);
|
|
posthog.group("project", project.id);
|
|
}
|
|
});
|
|
|
|
//page view
|
|
useEffect(() => {
|
|
if (postHogInitialized.current === false) return;
|
|
posthog.capture("$pageview");
|
|
}, [location, logging]);
|
|
};
|
|
|
|
export function usePostHogTracking() {
|
|
const capture = useCallback((eventName: string, properties?: Record<string, unknown>) => {
|
|
posthog.capture(eventName, properties);
|
|
}, []);
|
|
|
|
const startSessionRecording = useCallback(() => {
|
|
posthog.startSessionRecording();
|
|
}, []);
|
|
|
|
return { capture, startSessionRecording };
|
|
}
|