import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
* A reusable hook for triggering custom reload logic when specific SSE events occur.
*
* This hook is ideal when:
* - Your component displays derived/related data that isn't directly managed by react-admin
* - You need custom loading logic that goes beyond simple dataProvider.getMany() calls
* - Your data comes from non-standard endpoints or requires special processing
* - You want to reload parent/related resources when child resources change
*
* @param {Object} options - Configuration options
* @param {Array<string>} options.events - Array of event types to listen for (e.g., ['library', 'user', '*'])
* @param {Function} options.onRefresh - Async function to call when events occur.
* Should be wrapped in useCallback with appropriate dependencies to avoid unnecessary re-renders.
*
* @example
* // Example 1: LibrarySelector - Reload user data when library changes
* const loadUserLibraries = useCallback(async () => {
* const userId = localStorage.getItem('userId')
* if (userId) {
* const { data } = await dataProvider.getOne('user', { id: userId })
* dispatch(setUserLibraries(data.libraries || []))
* }
* }, [dataProvider, dispatch])
*
* useRefreshOnEvents({
* events: ['library', 'user'],
* onRefresh: loadUserLibraries
* })
*
* @example
* // Example 2: Statistics Dashboard - Reload stats when any music data changes
* const loadStats = useCallback(async () => {
* const stats = await dataProvider.customRequest('GET', 'stats')
* setDashboardStats(stats)
* }, [dataProvider, setDashboardStats])
*
* useRefreshOnEvents({
* events: ['album', 'song', 'artist'],
* onRefresh: loadStats
* })
*
* @example
* // Example 3: Permission-based UI - Reload permissions when user changes
* const loadPermissions = useCallback(async () => {
* const authData = await authProvider.getPermissions()
* setUserPermissions(authData)
* }, [authProvider, setUserPermissions])
*
* useRefreshOnEvents({
* events: ['user'],
* onRefresh: loadPermissions
* })
*
* @example
* // Example 4: Listen to all events (use sparingly)
* const reloadAll = useCallback(async () => {
* // This will trigger on ANY refresh event
* await reloadEverything()
* }, [reloadEverything])
*
* useRefreshOnEvents({
* events: ['*'],
* onRefresh: reloadAll
* })
*/
export const useRefreshOnEvents = ({ events, onRefresh }) => {
const [lastRefreshTime, setLastRefreshTime] = useState(Date.now())
const refreshData = useSelector(
(state) => state.activity?.refresh || { lastReceived: lastRefreshTime },
)
useEffect(() => {
const { resources, lastReceived } = refreshData
if (lastReceived <= lastRefreshTime) {
return
}
const shouldRefresh =
resources &&
(resources['*'] === '*' ||
events.some((eventType) => {
if (eventType === '*') {
return true
}
return resources[eventType]
}))
if (shouldRefresh) {
setLastRefreshTime(lastReceived)
if (onRefresh) {
onRefresh().catch((error) => {
console.warn('Error in useRefreshOnEvents onRefresh callback:', error)
})
}
}
}, [refreshData, lastRefreshTime, events, onRefresh])
}