Skip to content

Commit

Permalink
perf: Make history manager provider render only with subscribe
Browse files Browse the repository at this point in the history
  • Loading branch information
memoyil committed Aug 6, 2024
1 parent 5bca087 commit a6f12b5
Showing 1 changed file with 62 additions and 10 deletions.
72 changes: 62 additions & 10 deletions apps/web/src/contexts/HistoryContext.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,66 @@
import { useRouter } from 'next/router'
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'

const historyManagerContext = createContext<ReturnType<typeof useHistoryManager> | null>(null)
interface HistoryManagerContext {
history: string[]
canGoBack: () => boolean
subscribe: (subscriber: Subscriber) => void
unsubscribe: (subscriber: Subscriber) => void
}

interface Subscriber {
setState: React.Dispatch<React.SetStateAction<any>>
}

export function HistoryManagerProvider({ children }) {
const historyManagerContext = createContext<HistoryManagerContext | null>(null)

export function HistoryManagerProvider({ children }: { children: React.ReactNode }) {
const value = useHistoryManager()
return <historyManagerContext.Provider value={value}>{children}</historyManagerContext.Provider>
}

export const useHistory = () => useContext(historyManagerContext)
export const useHistory = () => {
const context = useContext(historyManagerContext)
if (!context) {
throw new Error('useHistory must be used within a HistoryManagerProvider')
}

const [, setState] = useState()

useEffect(() => {
const subscriber = { setState }
context.subscribe(subscriber)

return () => {
context.unsubscribe(subscriber)
}
}, [context])

function useHistoryManager() {
return context.history
}

function useHistoryManager(): HistoryManagerContext {
const router = useRouter()
const [history, setHistory] = useState<string[]>(() => [router?.asPath])
const subscribersRef = useRef<Subscriber[]>([])

useEffect(() => {
const handleRouteChange = (url, { shallow }) => {
const handleRouteChange = (url: string, { shallow }: { shallow: boolean }) => {
if (!shallow) {
setHistory((prevState) => [...prevState, url])
setHistory((prevState) => {
const newHistory = [...prevState, url]
notifySubscribers(newHistory)
return newHistory
})
}
}

router.beforePopState(() => {
setHistory((prevState) => prevState.slice(0, -2))
setHistory((prevState) => {
const newHistory = prevState.slice(0, -2)
notifySubscribers(newHistory)
return newHistory
})
return true
})

Expand All @@ -34,7 +72,21 @@ function useHistoryManager() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const notifySubscribers = useCallback((newHistory: string[]) => {
subscribersRef.current.forEach((subscriber) => {
subscriber.setState(newHistory)
})
}, [])

const subscribe = useCallback((subscriber: Subscriber) => {
subscribersRef.current.push(subscriber)
}, [])

const unsubscribe = useCallback((subscriber: Subscriber) => {
subscribersRef.current = subscribersRef.current.filter((sub) => sub !== subscriber)
}, [])

return useMemo(() => {
return { history, canGoBack: () => history.length > 1 }
}, [history])
return { history, canGoBack: () => history.length > 1, subscribe, unsubscribe }
}, [history, subscribe, unsubscribe])
}

0 comments on commit a6f12b5

Please sign in to comment.