import { useCallback , useMemo } from 'react' ; import ReactDOM from 'react-dom' ; import { useSelector } from 'react-redux' ; import { createSelector } from 'reselect' ; import debounce from 'lodash/debounce' ; import union from 'lodash/union' ; import isEqual from 'lodash/isEqual' ; import get from 'lodash/get' ; import { CRUD_GET_MANY } from '../actions/dataActions/crudGetMany' ; import { Identifier , Record , ReduxState , DataProviderProxy } from '../types' ; import { useSafeSetState } from '../util/hooks' ; import useDataProvider from './useDataProvider' ; import { useEffect } from 'react' ; import { useVersion } from '../controller' ; import { Refetch } from './useQueryWithStore' ; type Callback = ( args ? : any ) => void ; type SetState = ( args : any ) => void ; interface Query { ids : Identifier [ ] ; onSuccess : Callback ; onFailure : Callback ; setState : SetState ; } interface QueriesToCall { [ resource ] : Query [ ] ; } interface UseGetManyOptions { onSuccess ? : Callback ; onFailure ? : Callback ; enabled ? : boolean ; } interface UseGetManyResult { data : Record [ ] ; error ? : any ; loading : boolean ; loaded : boolean ; refetch : Refetch ; } let queriesToCall = { } ; let dataProvider ; const DataProviderOptions = { action : CRUD_GET_MANY } ; param resource param ids param options param { boolean } options . enabled param { Function } options . onSuccess param { Function } options . onFailure returns example const useGetMany = ( resource , ids , options = { enabled : true } ) : UseGetManyResult => { const selectMany = useMemo ( makeGetManySelector , [ ] ) ; const data = useSelector ( ( state ) => selectMany ( state , resource , ids ) ) ; const version = useVersion ( ) ; const [ innerVersion , setInnerVersion ] = useSafeSetState ( 0 ) ; const refetch = useCallback ( ( ) => { setInnerVersion ( prevInnerVersion => prevInnerVersion + 1 ) ; } , [ setInnerVersion ] ) ; const [ state , setState ] = useSafeSetState ( { data , error : null , loading : ids . length !== 0 , loaded : data . length !== 0 && ! data . includes ( undefined ) , refetch , } ) ; if ( ! isEqual ( state . data , data ) ) { setState ( { ... state , data , } ) ; } dataProvider = useDataProvider ( ) ; useEffect ( ( ) => { if ( options . enabled === false ) { return ; } if ( ! queriesToCall [ resource ] ) { queriesToCall [ resource ] = [ ] ; } /**
         * queriesToCall stores the queries to call under the following shape:
         *
         * {
         *   'posts': [
         *     { ids: [1, 2], setState }
         *     { ids: [2, 3], setState, onSuccess }
         *     { ids: [4, 5], setState }
         *   ],
         *   'comments': [
         *     { ids: [345], setState, onFailure }
         *   ]
         * }
         */ queriesToCall [ resource ] = queriesToCall [ resource ] . concat ( { ids , setState , onSuccess : options && options . onSuccess , onFailure : options && options . onFailure , } ) ; callQueries ( ) ; } , [ JSON . stringify ( { resource , ids , options , version , innerVersion , } ) , dataProvider , ] ) ; return state ; } ; see https const makeGetManySelector = ( ) => createSelector ( [ ( state , resource ) => get ( state , [ 'admin' , 'resources' , resource , 'data' ] ) , ( _ , __ , ids ) => ids , ] , ( resourceData , ids ) => resourceData ? ids . map ( id => resourceData [ id ] ) : ids . map ( id => undefined ) ) ; /**
 * Call the dataProvider once per resource
 */ const callQueries = debounce ( ( ) => { const resources = Object . keys ( queriesToCall ) ; resources . forEach ( resource => { const queries = [ ... queriesToCall [ resource ] ] ; example const accumulatedIds = queries . reduce ( ( acc , { ids } ) => union ( acc , ids ) , [ ] ) . filter ( v => v != null && v !== '' ) ; if ( accumulatedIds . length === 0 ) { queries . forEach ( ( { ids , setState , onSuccess } ) => { setState ( { data : emptyArray , loading : false , loaded : true , } ) ; if ( onSuccess ) { onSuccess ( { data : emptyArray } ) ; } } ) ; return ; } dataProvider . getMany ( resource , { ids : accumulatedIds } , DataProviderOptions ) . then ( response => ReactDOM . unstable_batchedUpdates ( ( ) => queries . forEach ( ( { ids , setState , onSuccess } ) => { setState ( prevState => ( { ... prevState , error : null , loading : false , loaded : true , } ) ) ; if ( onSuccess ) { const subData = ids . map ( id => response . data . find ( datum => datum . id == id ) ) ; onSuccess ( { data : subData } ) ; } } ) ) ) . catch ( error => ReactDOM . unstable_batchedUpdates ( ( ) => queries . forEach ( ( { setState , onFailure } ) => { setState ( { error , loading : false , loaded : false } ) ; onFailure && onFailure ( error ) ; } ) ) ) ; delete queriesToCall [ resource ] ; } ) ; } ) ; const emptyArray = [ ] ; export default useGetMany ;