62 lines
2.2 KiB
TypeScript
62 lines
2.2 KiB
TypeScript
import React, { createContext, useContext, useEffect, useRef } from 'react'
|
|
import { useLocation } from '@tanstack/react-router'
|
|
import { EventBus, REFRESH_EVENT } from './event-bus'
|
|
import { getQueryClient } from './query-client'
|
|
|
|
// Web port of packages/ui refresh-context + useManualRefresh +
|
|
// useMarkDataFetchers + useFocusCallback. Navigation focus (expo) is
|
|
// approximated with route-pathname changes; manual refresh uses the bus.
|
|
|
|
const RefreshContext = createContext<{ refreshAll: () => void } | null>(null)
|
|
|
|
export const RefreshProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const refreshAll = () => {
|
|
getQueryClient().invalidateQueries()
|
|
EventBus.emit(REFRESH_EVENT)
|
|
}
|
|
return <RefreshContext.Provider value={{ refreshAll }}>{children}</RefreshContext.Provider>
|
|
}
|
|
|
|
export const useRefresh = () => {
|
|
const context = useContext(RefreshContext)
|
|
if (!context) throw new Error('useRefresh must be used within RefreshProvider')
|
|
return context
|
|
}
|
|
|
|
export function useManualRefresh(callback: () => void) {
|
|
const ref = useRef(callback)
|
|
ref.current = callback
|
|
useEffect(() => {
|
|
const sub = EventBus.addListener(REFRESH_EVENT, () => ref.current())
|
|
return () => sub.remove()
|
|
}, [])
|
|
}
|
|
|
|
// Refetch on 2nd+ visit to the current route (mirrors useMarkDataFetchers:
|
|
// skip first focus, refetch on subsequent focuses).
|
|
export function useMarkDataFetchers(callback: () => void) {
|
|
const location = useLocation()
|
|
const ref = useRef(callback)
|
|
ref.current = callback
|
|
const visitsRef = useRef<Record<string, number>>({})
|
|
useEffect(() => {
|
|
const key = location.pathname + location.searchStr
|
|
const count = visitsRef.current[key] ?? 0
|
|
visitsRef.current[key] = count + 1
|
|
if (count >= 1) {
|
|
ref.current()
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [location.pathname, location.searchStr])
|
|
}
|
|
|
|
// Run callback when the window regains focus (web analogue of screen focus).
|
|
export default function useFocusCallback(callback: () => void) {
|
|
const ref = useRef(callback)
|
|
ref.current = callback
|
|
useEffect(() => {
|
|
const onFocus = () => ref.current()
|
|
window.addEventListener('focus', onFocus)
|
|
return () => window.removeEventListener('focus', onFocus)
|
|
}, [])
|
|
}
|