-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make usePageContext
return the correct path no matter where its called
#21433
Changes from all commits
3a89409
7d1cc93
2ec554b
2fcd67a
fa84fb1
4ff668b
14cfd1c
c39367f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,11 @@ | ||
import {createContext, useCallback, useContext, useEffect, useMemo} from 'react'; | ||
import {createContext, useCallback, useContext, useLayoutEffect} from 'react'; | ||
import {useLocation, useRouteMatch} from 'react-router-dom'; | ||
import {atom, useRecoilValue, useSetRecoilState} from 'recoil'; | ||
|
||
export const currentPageAtom = atom<{path: string; specificPath: string}>({ | ||
key: 'currentPageAtom', | ||
default: {path: '/', specificPath: '/'}, | ||
}); | ||
|
||
export interface GenericAnalytics { | ||
group?: (groupId: string, traits?: Record<string, any>) => void; | ||
|
@@ -13,10 +19,7 @@ export const AnalyticsContext = createContext<GenericAnalytics>(undefined!); | |
const PAGEVIEW_DELAY = 300; | ||
|
||
export const usePageContext = () => { | ||
const match = useRouteMatch(); | ||
const {pathname: specificPath} = useLocation(); | ||
const {path} = match; | ||
return useMemo(() => ({path, specificPath}), [path, specificPath]); | ||
return useRecoilValue(currentPageAtom); | ||
}; | ||
|
||
const useAnalytics = () => { | ||
|
@@ -52,28 +55,35 @@ export const dummyAnalytics = () => ({ | |
|
||
export const useTrackPageView = () => { | ||
const analytics = useAnalytics(); | ||
const {path, specificPath} = usePageContext(); | ||
const match = useRouteMatch(); | ||
const {pathname: specificPath} = useLocation(); | ||
const {path} = match; | ||
|
||
const setCurrentPage = useSetRecoilState(currentPageAtom); | ||
|
||
useEffect(() => { | ||
useLayoutEffect(() => { | ||
// Wait briefly to allow redirects. | ||
const timer = setTimeout(() => { | ||
analytics.page(path, specificPath); | ||
}, PAGEVIEW_DELAY); | ||
setCurrentPage({path, specificPath}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This all looks good and I think I'm in favor of the formal React solution. That said, an alternative two line version of this PR would be to set a hidden There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This wouldn't be equivalent because changing |
||
|
||
return () => { | ||
clearTimeout(timer); | ||
}; | ||
}, [analytics, path, specificPath]); | ||
}, [analytics, path, setCurrentPage, specificPath]); | ||
}; | ||
|
||
export const useTrackEvent = () => { | ||
const analytics = useAnalytics(); | ||
const pathValues = usePageContext(); | ||
const match = useRouteMatch(); | ||
const {pathname: specificPath} = useLocation(); | ||
const {path} = match; | ||
|
||
return useCallback( | ||
(eventName: string, properties?: Record<string, any>) => { | ||
analytics.track(eventName, {...properties, ...pathValues}); | ||
analytics.track(eventName, {...properties, path, specificPath}); | ||
}, | ||
[analytics, pathValues], | ||
[analytics, path, specificPath], | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used a recoil atom here to avoid having yet another provider and having to order them properly (which in some cases necessitates another provider yay). Cloud already uses Recoil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure I follow how Recoil avoids another provider / ordering problem, this PR adds a RecoilProvider?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Recoil provider is at the root, that's pretty much the extent of ordering that we need to think about.