UNPKG

ra-core

Version:

Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React

166 lines (155 loc) 6.25 kB
import { useContext, useMemo } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import DataProviderContext from './DataProviderContext'; import { defaultDataProvider } from './defaultDataProvider'; import validateResponseFormat from './validateResponseFormat'; import { DataProvider } from '../types'; import useLogoutIfAccessDenied from '../auth/useLogoutIfAccessDenied'; import { reactAdminFetchActions } from './dataFetchActions'; import { populateQueryCache } from './populateQueryCache'; /** * Hook for getting a dataProvider * * Gets a dataProvider object, which behaves just like the real dataProvider * (same methods returning a Promise). But it's actually a Proxy object, * which validates the response format, and logs the user out upon error * if authProvider.checkError() rejects. * * @return dataProvider * * @example Basic usage * * import * as React from 'react'; * import { useState } from 'react'; * import { useDataProvider } from 'react-admin'; * * const PostList = () => { * const [posts, setPosts] = useState([]) * const dataProvider = useDataProvider(); * useEffect(() => { * dataProvider.getList('posts', { filter: { status: 'pending' }}) * .then(({ data }) => setPosts(data)); * }, []) * * return ( * <Fragment> * {posts.map((post, key) => <PostDetail post={post} key={key} />)} * </Fragment> * ); * } * * @example Handling all states (loading, error, success) * * import { useState, useEffect } from 'react'; * import { useDataProvider } from 'react-admin'; * * const UserProfile = ({ userId }) => { * const dataProvider = useDataProvider(); * const [user, setUser] = useState(); * const [loading, setLoading] = useState(true); * const [error, setError] = useState(); * useEffect(() => { * dataProvider.getOne('users', { id: userId }) * .then(({ data }) => { * setUser(data); * setLoading(false); * }) * .catch(error => { * setError(error); * setLoading(false); * }) * }, []); * * if (loading) return <Loading />; * if (error) return <Error /> * if (!user) return null; * * return ( * <ul> * <li>Name: {user.name}</li> * <li>Email: {user.email}</li> * </ul> * ) * } */ const arrayReturnTypes = ['getList', 'getMany', 'getManyReference']; export const useDataProvider = < TDataProvider extends DataProvider = DataProvider, >(): TDataProvider => { const dataProvider = (useContext(DataProviderContext) || defaultDataProvider) as unknown as TDataProvider; const queryClient = useQueryClient(); const logoutIfAccessDenied = useLogoutIfAccessDenied(); const dataProviderProxy = useMemo(() => { return new Proxy(dataProvider, { get: (_, name) => { if (typeof name === 'symbol' || name === 'then') { return; } if (name === 'supportAbortSignal') { return dataProvider.supportAbortSignal; } return (...args) => { const type = name.toString(); if (typeof dataProvider[type] !== 'function') { throw new Error( `Unknown dataProvider function: ${type}` ); } try { return dataProvider[type] .apply(dataProvider, args) .then(response => { if ( process.env.NODE_ENV === 'development' && reactAdminFetchActions.includes(type) ) { validateResponseFormat(response, type); } if (response?.meta?.prefetched) { populateQueryCache({ data: response?.meta.prefetched, queryClient, }); } return response; }) .catch(error => { if ( process.env.NODE_ENV !== 'production' && // do not log AbortErrors !isAbortError(error) ) { console.error(error); } return logoutIfAccessDenied(error).then( loggedOut => { if (loggedOut) return { data: arrayReturnTypes.includes( type ) ? [] : {}, }; throw error; } ); }); } catch (e) { if (process.env.NODE_ENV !== 'production') { console.error(e); } throw new Error( 'The dataProvider threw an error. It should return a rejected Promise instead.' ); } }; }, }); }, [dataProvider, logoutIfAccessDenied, queryClient]); return dataProviderProxy; }; const isAbortError = error => error instanceof DOMException && (error as DOMException).name === 'AbortError';