diff --git a/docs/Actions.md b/docs/Actions.md index effe4812e0a..37ac096fba3 100644 --- a/docs/Actions.md +++ b/docs/Actions.md @@ -147,9 +147,9 @@ The return value of `useQuery` is an object representing the query state, using This object updates according to the request state: -- start: `{ loading: true, loaded: false }` -- success: `{ data: [data from response], total: [total from response], loading: false, loaded: true }` -- error: `{ error: [error from response], loading: false, loaded: true }` +- start: `{ loading: true, loaded: false, refetch }` +- success: `{ data: [data from response], total: [total from response], loading: false, loaded: true, refetch }` +- error: `{ error: [error from response], loading: false, loaded: true, refetch }` As a reminder, here are the read query types handled by Data Providers: @@ -299,7 +299,7 @@ const { data, loaded } = useGetOne('products', 123); ```jsx // syntax -const { data, ids, total, loading, loaded, error } = useGetList(resource, pagination, sort, filter, options); +const { data, ids, total, loading, loaded, error, refetch } = useGetList(resource, pagination, sort, filter, options); // example import { useGetList } from 'react-admin'; @@ -325,7 +325,7 @@ const LatestNews = () => { ```jsx // syntax -const { data, loading, loaded, error } = useGetOne(resource, id, options); +const { data, loading, loaded, error, refetch } = useGetOne(resource, id, options); // example import { useGetOne } from 'react-admin'; @@ -341,7 +341,7 @@ const UserProfile = ({ record }) => { ```jsx // syntax -const { data, loading, loaded, error } = useGetMany(resource, ids, options); +const { data, loading, loaded, error, refetch } = useGetMany(resource, ids, options); // example import { useGetMany } from 'react-admin'; @@ -363,7 +363,7 @@ const PostTags = ({ record }) => { ```jsx // syntax -const { data, ids, total, loading, loaded, error } = useGetManyReference(resource, target, id, pagination, sort, filter, referencingResource, options); +const { data, ids, total, loading, loaded, error, refetch } = useGetManyReference(resource, target, id, pagination, sort, filter, referencingResource, options); // example import { useGetManyReference } from 'react-admin'; diff --git a/docs/List.md b/docs/List.md index 1e43812c173..6c94abbe946 100644 --- a/docs/List.md +++ b/docs/List.md @@ -1930,6 +1930,7 @@ const { basePath, // deduced from the location, useful for action buttons defaultTitle, // the translated title based on the resource, e.g. 'Posts' resource, // the resource name, deduced from the location. e.g. 'posts' + refetch, // a callback to refresh the list data } = useListContext(); ``` diff --git a/packages/ra-core/src/controller/ListContext.tsx b/packages/ra-core/src/controller/ListContext.tsx index 7792905d182..985ea740d34 100644 --- a/packages/ra-core/src/controller/ListContext.tsx +++ b/packages/ra-core/src/controller/ListContext.tsx @@ -31,6 +31,7 @@ import { ListControllerProps } from './useListController'; * @prop {string} basePath deduced from the location, useful for action buttons * @prop {string} defaultTitle the translated title based on the resource, e.g. 'Posts' * @prop {string} resource the resource name, deduced from the location. e.g. 'posts' + * @prop {Function} refetch a function for triggering a refetch of the list data * * @typedef Props * @prop {ListControllerProps} value @@ -70,6 +71,7 @@ const ListContext = createContext({ onUnselectItems: null, page: null, perPage: null, + refetch: null, resource: null, selectedIds: null, setFilters: null, diff --git a/packages/ra-core/src/controller/details/EditContext.tsx b/packages/ra-core/src/controller/details/EditContext.tsx index c8196f45f94..f7b6c48475b 100644 --- a/packages/ra-core/src/controller/details/EditContext.tsx +++ b/packages/ra-core/src/controller/details/EditContext.tsx @@ -32,6 +32,7 @@ export const EditContext = createContext({ setOnFailure: null, setOnSuccess: null, setTransform: null, + refetch: null, resource: null, save: null, saving: null, diff --git a/packages/ra-core/src/controller/details/ShowContext.tsx b/packages/ra-core/src/controller/details/ShowContext.tsx index c263407a5cb..160b8995bde 100644 --- a/packages/ra-core/src/controller/details/ShowContext.tsx +++ b/packages/ra-core/src/controller/details/ShowContext.tsx @@ -25,6 +25,7 @@ export const ShowContext = createContext({ defaultTitle: null, loaded: null, loading: null, + refetch: null, resource: null, version: null, }); diff --git a/packages/ra-core/src/controller/details/useEditController.ts b/packages/ra-core/src/controller/details/useEditController.ts index a611d1d9c06..d300fd6ceba 100644 --- a/packages/ra-core/src/controller/details/useEditController.ts +++ b/packages/ra-core/src/controller/details/useEditController.ts @@ -15,7 +15,7 @@ import { useRefresh, RedirectionSideEffect, } from '../../sideEffect'; -import { useGetOne, useUpdate } from '../../dataProvider'; +import { useGetOne, useUpdate, Refetch } from '../../dataProvider'; import { useTranslate } from '../../i18n'; import { CRUD_GET_ONE, CRUD_UPDATE } from '../../actions'; import { @@ -74,6 +74,7 @@ export interface EditControllerProps { setTransform: SetTransformData; successMessage?: string; record?: RecordType; + refetch: Refetch; redirect: RedirectionSideEffect; resource: string; version: number; @@ -137,7 +138,7 @@ export const useEditController = ( setTransform, } = useSaveModifiers({ onSuccess, onFailure, transform }); - const { data: record, loading, loaded } = useGetOne( + const { data: record, loading, loaded, refetch } = useGetOne( resource, id, { @@ -262,6 +263,7 @@ export const useEditController = ( setOnSuccess, setOnFailure, setTransform, + refetch, resource, basePath, record, diff --git a/packages/ra-core/src/controller/details/useShowController.ts b/packages/ra-core/src/controller/details/useShowController.ts index 2991f3b52e8..1f9fb0167b3 100644 --- a/packages/ra-core/src/controller/details/useShowController.ts +++ b/packages/ra-core/src/controller/details/useShowController.ts @@ -1,7 +1,7 @@ import useVersion from '../useVersion'; import { useCheckMinimumRequiredProps } from '../checkMinimumRequiredProps'; import { Record, Identifier } from '../../types'; -import { useGetOne } from '../../dataProvider'; +import { useGetOne, Refetch } from '../../dataProvider'; import { useTranslate } from '../../i18n'; import { useNotify, useRedirect, useRefresh } from '../../sideEffect'; import { CRUD_GET_ONE } from '../../actions'; @@ -32,6 +32,7 @@ export interface ShowControllerProps { hasShow?: boolean; resource: string; record?: RecordType; + refetch: Refetch; version: number; } @@ -63,7 +64,7 @@ export const useShowController = ( const redirect = useRedirect(); const refresh = useRefresh(); const version = useVersion(); - const { data: record, loading, loaded } = useGetOne( + const { data: record, loading, loaded, refetch } = useGetOne( resource, id, { @@ -90,6 +91,7 @@ export const useShowController = ( resource, basePath, record, + refetch, hasCreate, hasEdit, hasList, diff --git a/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts b/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts index f37a85b2a74..5aa4354be50 100644 --- a/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts +++ b/packages/ra-core/src/controller/field/useReferenceArrayFieldController.ts @@ -70,23 +70,27 @@ const useReferenceArrayFieldController = ( const resource = useResourceContext(props); const notify = useNotify(); const ids = get(record, source) || emptyArray; - const { data, error, loading, loaded } = useGetMany(reference, ids, { - onFailure: error => - notify( - typeof error === 'string' - ? error - : error.message || 'ra.notification.http_error', - 'warning', - { - _: - typeof error === 'string' - ? error - : error && error.message - ? error.message - : undefined, - } - ), - }); + const { data, error, loading, loaded, refetch } = useGetMany( + reference, + ids, + { + onFailure: error => + notify( + typeof error === 'string' + ? error + : error.message || 'ra.notification.http_error', + 'warning', + { + _: + typeof error === 'string' + ? error + : error && error.message + ? error.message + : undefined, + } + ), + } + ); const [loadingState, setLoadingState] = useSafeSetState(loading); const [loadedState, setLoadedState] = useSafeSetState(loaded); @@ -246,6 +250,7 @@ const useReferenceArrayFieldController = ( onUnselectItems, page, perPage, + refetch, resource: reference, selectedIds, setFilters, diff --git a/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts b/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts index 534552c2162..27a21d29ff8 100644 --- a/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts +++ b/packages/ra-core/src/controller/field/useReferenceManyFieldController.ts @@ -158,7 +158,15 @@ const useReferenceManyFieldController = ( }); const referenceId = get(record, source); - const { data, ids, total, error, loading, loaded } = useGetManyReference( + const { + data, + ids, + total, + error, + loading, + loaded, + refetch, + } = useGetManyReference( reference, target, referenceId, @@ -205,6 +213,7 @@ const useReferenceManyFieldController = ( onUnselectItems, page, perPage, + refetch, resource: reference, selectedIds, setFilters, diff --git a/packages/ra-core/src/controller/input/ReferenceInputController.spec.tsx b/packages/ra-core/src/controller/input/ReferenceInputController.spec.tsx index efd32ec5425..c6d02a16ebb 100644 --- a/packages/ra-core/src/controller/input/ReferenceInputController.spec.tsx +++ b/packages/ra-core/src/controller/input/ReferenceInputController.spec.tsx @@ -194,6 +194,7 @@ describe('', () => { loading: true, page: 1, perPage: 25, + refetch: expect.any(Function), resource: 'comments', selectedIds: [], @@ -206,6 +207,7 @@ describe('', () => { error: null, loaded: true, loading: true, + refetch: expect.any(Function), }, dataStatus: { error: null, diff --git a/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts b/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts index 28c6339d349..c87cf4ef0dc 100644 --- a/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts +++ b/packages/ra-core/src/controller/input/useReferenceArrayInputController.ts @@ -240,10 +240,11 @@ export const useReferenceArrayInputController = ( [queryFilter, defaultFilter, filterToQuery] ); - const { data: referenceRecordsFetched, loaded } = useGetMany( - reference, - idsToFetch || [] - ); + const { + data: referenceRecordsFetched, + loaded, + refetch: refetchGetMany, + } = useGetMany(reference, idsToFetch || []); const referenceRecords = referenceRecordsFetched ? referenceRecordsFetched.concat(referenceRecordsFromStore) @@ -256,6 +257,7 @@ export const useReferenceArrayInputController = ( data: matchingReferences, ids: matchingReferencesIds, total, + refetch: refetchGetMatching, } = useGetMatching( reference, pagination, @@ -282,6 +284,11 @@ export const useReferenceArrayInputController = ( translate, }); + const refetch = useCallback(() => { + refetchGetMany(); + refetchGetMatching(); + }, [refetchGetMany, refetchGetMatching]); + return { basePath: props.basePath || `/${resource}`, choices: dataStatus.choices, @@ -307,6 +314,7 @@ export const useReferenceArrayInputController = ( onUnselectItems, page, perPage, + refetch, resource, selectedIds: input.value, setFilter, diff --git a/packages/ra-core/src/controller/input/useReferenceInputController.ts b/packages/ra-core/src/controller/input/useReferenceInputController.ts index 2a70e2fb773..ef0355d75fb 100644 --- a/packages/ra-core/src/controller/input/useReferenceInputController.ts +++ b/packages/ra-core/src/controller/input/useReferenceInputController.ts @@ -122,12 +122,13 @@ export const useReferenceInputController = ( loaded: possibleValuesLoaded, loading: possibleValuesLoading, error: possibleValuesError, + refetch: refetchGetList, } = useGetList(reference, pagination, sort, filterValues); // fetch current value const { referenceRecord, - refetch, + refetch: refetchReference, error: referenceError, loading: referenceLoading, loaded: referenceLoaded, @@ -158,6 +159,11 @@ export const useReferenceInputController = ( translate, }); + const refetch = useCallback(() => { + refetchGetList(); + refetchReference(); + }, [refetchGetList, refetchReference]); + return { // should match the ListContext shape possibleValues: { @@ -184,6 +190,7 @@ export const useReferenceInputController = ( onSelect, onToggleItem, onUnselectItems, + refetch, resource, }, referenceRecord: { @@ -191,6 +198,7 @@ export const useReferenceInputController = ( loaded: referenceLoaded, loading: referenceLoading, error: referenceError, + refetch: refetchReference, }, dataStatus: { error: dataStatus.error, @@ -224,6 +232,7 @@ export interface ReferenceInputValue { loaded: boolean; loading: boolean; error?: any; + refetch: Refetch; }; dataStatus: { error?: any; diff --git a/packages/ra-core/src/controller/useListContext.ts b/packages/ra-core/src/controller/useListContext.ts index 4e7b8601ecc..91f8aaeb014 100644 --- a/packages/ra-core/src/controller/useListContext.ts +++ b/packages/ra-core/src/controller/useListContext.ts @@ -135,6 +135,7 @@ const extractListContextProps = ({ onUnselectItems, page, perPage, + refetch, resource, selectedIds, setFilters, @@ -160,6 +161,7 @@ const extractListContextProps = ({ onUnselectItems, page, perPage, + refetch, resource, selectedIds, setFilters, diff --git a/packages/ra-core/src/controller/useListController.ts b/packages/ra-core/src/controller/useListController.ts index 40d89db634b..f33b694156e 100644 --- a/packages/ra-core/src/controller/useListController.ts +++ b/packages/ra-core/src/controller/useListController.ts @@ -7,6 +7,7 @@ import useRecordSelection from './useRecordSelection'; import useTranslate from '../i18n/useTranslate'; import useNotify from '../sideEffect/useNotify'; import { useGetMainList } from '../dataProvider/useGetMainList'; +import { Refetch } from '../dataProvider/useQueryWithStore'; import { SORT_ASC } from '../reducer/admin/resource/list/queryReducer'; import { CRUD_GET_LIST } from '../actions'; import defaultExporter from '../export/defaultExporter'; @@ -69,6 +70,7 @@ export interface ListControllerProps { onUnselectItems: () => void; page: number; perPage: number; + refetch: Refetch; resource: string; selectedIds: Identifier[]; setFilters: ( @@ -147,9 +149,15 @@ const useListController = ( * We want the list of ids to be always available for optimistic rendering, * and therefore we need a custom action (CRUD_GET_LIST) that will be used. */ - const { ids, data, total, error, loading, loaded } = useGetMainList< - RecordType - >( + const { + ids, + data, + total, + error, + loading, + loaded, + refetch, + } = useGetMainList( resource, { page: query.page, @@ -226,6 +234,7 @@ const useListController = ( onUnselectItems: selectionModifiers.clearSelection, page: query.page, perPage: query.perPage, + refetch, resource, selectedIds, setFilters: queryModifiers.setFilters, @@ -256,6 +265,7 @@ export const injectedProps = [ 'onUnselectItems', 'page', 'perPage', + 'refetch', 'refresh', 'resource', 'selectedIds', diff --git a/packages/ra-core/src/dataProvider/useGetList.spec.tsx b/packages/ra-core/src/dataProvider/useGetList.spec.tsx index 30cd57a58fd..c0139adb809 100644 --- a/packages/ra-core/src/dataProvider/useGetList.spec.tsx +++ b/packages/ra-core/src/dataProvider/useGetList.spec.tsx @@ -120,7 +120,7 @@ describe('useGetList', () => { }, } ); - expect(hookValue.mock.calls[0][0]).toEqual({ + expect(hookValue.mock.calls[0][0]).toMatchObject({ data: { 1: { id: 1 }, 2: { id: 2 } }, ids: [1, 2], total: 2, @@ -167,7 +167,7 @@ describe('useGetList', () => { } ); await waitFor(() => { - expect(hookValue.mock.calls.pop()[0]).toEqual({ + expect(hookValue.mock.calls.pop()[0]).toMatchObject({ data: { 1: { id: 1, title: 'foo' }, 2: { id: 2, title: 'bar' }, @@ -229,7 +229,7 @@ describe('useGetList', () => { resources: { posts: { data: {}, cachedRequests: {} } }, }, }); - expect(hookValue.mock.calls[0][0]).toEqual({ + expect(hookValue.mock.calls[0][0]).toMatchObject({ data: {}, ids: [], total: undefined, diff --git a/packages/ra-core/src/dataProvider/useGetList.ts b/packages/ra-core/src/dataProvider/useGetList.ts index 1ade5dfe97f..540b4309b9b 100644 --- a/packages/ra-core/src/dataProvider/useGetList.ts +++ b/packages/ra-core/src/dataProvider/useGetList.ts @@ -10,7 +10,7 @@ import { RecordMap, UseDataProviderOptions, } from '../types'; -import { useQueryWithStore } from './useQueryWithStore'; +import { useQueryWithStore, Refetch } from './useQueryWithStore'; const defaultPagination = { page: 1, perPage: 25 }; const defaultSort = { field: 'id', order: 'DESC' }; @@ -24,9 +24,9 @@ const defaultData = {}; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -40,7 +40,7 @@ const defaultData = {}; * @param {Function} options.onSuccess Side effect function to be executed upon success, e.g. { onSuccess: { refresh: true } } * @param {Function} options.onFailure Side effect function to be executed upon failure, e.g. { onFailure: error => notify(error.message) } * - * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded }. + * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded, refetch }. * * @example * @@ -72,6 +72,7 @@ const useGetList = ( error?: any; loading: boolean; loaded: boolean; + refetch: Refetch; } => { const requestSignature = JSON.stringify({ pagination, sort, filter }); @@ -81,6 +82,7 @@ const useGetList = ( error, loading, loaded, + refetch, } = useQueryWithStore( { type: 'getList', resource, payload: { pagination, sort, filter } }, options, @@ -95,7 +97,7 @@ const useGetList = ( state.admin.resources, [resource, 'data'], defaultData - ), + ) as RecordMap, }), // total selector (may return undefined) (state: ReduxState): number => @@ -130,6 +132,7 @@ const useGetList = ( error, loading, loaded, + refetch, }; }; diff --git a/packages/ra-core/src/dataProvider/useGetMainList.tsx b/packages/ra-core/src/dataProvider/useGetMainList.tsx index de6d7ce27a1..c2c86e48ae2 100644 --- a/packages/ra-core/src/dataProvider/useGetMainList.tsx +++ b/packages/ra-core/src/dataProvider/useGetMainList.tsx @@ -9,7 +9,7 @@ import { Record, RecordMap, } from '../types'; -import { useQueryWithStore } from './useQueryWithStore'; +import { useQueryWithStore, Refetch } from './useQueryWithStore'; const defaultIds = []; const defaultData = {}; @@ -23,9 +23,9 @@ const defaultData = {}; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -36,7 +36,7 @@ const defaultData = {}; * @param {Object} filter The request filters, e.g. { title: 'hello, world' } * @param {Object} options Options object to pass to the dataProvider. May include side effects to be executed upon success or failure, e.g. { onSuccess: { refresh: true } } * - * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded }. + * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded, refetch }. * * @example * @@ -68,6 +68,7 @@ export const useGetMainList = ( error?: any; loading: boolean; loaded: boolean; + refetch: Refetch; } => { const requestSignature = JSON.stringify({ pagination, sort, filter }); const memo = useRef>({}); @@ -76,6 +77,7 @@ export const useGetMainList = ( error, loading, loaded, + refetch, } = useQueryWithStore( { type: 'getList', resource, payload: { pagination, sort, filter } }, options, @@ -123,7 +125,7 @@ export const useGetMainList = ( state.admin.resources, [resource, 'data'], defaultData - ); + ) as RecordMap; // poor man's useMemo inside a hook using a ref if ( memo.current.finalIds !== finalIds || @@ -164,6 +166,7 @@ export const useGetMainList = ( error, loading, loaded, + refetch, }; }; diff --git a/packages/ra-core/src/dataProvider/useGetMany.ts b/packages/ra-core/src/dataProvider/useGetMany.ts index 6252694be3c..d55a0b6bb6c 100644 --- a/packages/ra-core/src/dataProvider/useGetMany.ts +++ b/packages/ra-core/src/dataProvider/useGetMany.ts @@ -49,9 +49,9 @@ const DataProviderOptions = { action: CRUD_GET_MANY }; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -72,7 +72,7 @@ const DataProviderOptions = { action: CRUD_GET_MANY }; * @param {Function} options.onSuccess Side effect function to be executed upon success, e.g. { onSuccess: { refresh: true } } * @param {Function} options.onFailure Side effect function to be executed upon failure, e.g. { onFailure: error => notify(error.message) } * - * @returns The current request state. Destructure as { data, error, loading, loaded }. + * @returns The current request state. Destructure as { data, error, loading, loaded, refetch }. * * @example * diff --git a/packages/ra-core/src/dataProvider/useGetManyReference.ts b/packages/ra-core/src/dataProvider/useGetManyReference.ts index b3ea948b560..8bf50d7a5c5 100644 --- a/packages/ra-core/src/dataProvider/useGetManyReference.ts +++ b/packages/ra-core/src/dataProvider/useGetManyReference.ts @@ -33,9 +33,9 @@ interface UseGetManyReferenceOptions { * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -52,7 +52,7 @@ interface UseGetManyReferenceOptions { * @param {Function} options.onSuccess Side effect function to be executed upon success, e.g. { onSuccess: { refresh: true } } * @param {Function} options.onFailure Side effect function to be executed upon failure, e.g. { onFailure: error => notify(error.message) } * - * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded }. + * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded, refetch }. * * @example * @@ -96,6 +96,7 @@ const useGetManyReference = ( error, loading, loaded, + refetch, } = useQueryWithStore( { type: 'getManyReference', @@ -130,7 +131,15 @@ const useGetManyReference = ( [ids, allRecords] ); - return { data, ids: ids || defaultIds, total, error, loading, loaded }; + return { + data, + ids: ids || defaultIds, + total, + error, + loading, + loaded, + refetch, + }; }; interface DataSelectorResult { diff --git a/packages/ra-core/src/dataProvider/useGetMatching.ts b/packages/ra-core/src/dataProvider/useGetMatching.ts index 1738e81a92a..579e9083a38 100644 --- a/packages/ra-core/src/dataProvider/useGetMatching.ts +++ b/packages/ra-core/src/dataProvider/useGetMatching.ts @@ -9,7 +9,7 @@ import { Record, ReduxState, } from '../types'; -import { useQueryWithStore } from './useQueryWithStore'; +import { useQueryWithStore, Refetch } from './useQueryWithStore'; import { getReferenceResource, getPossibleReferenceValues, @@ -35,9 +35,9 @@ const referenceSource = (resource, source) => `${resource}@${source}`; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from store], ids: [ids from response], total: [total from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -53,7 +53,7 @@ const referenceSource = (resource, source) => `${resource}@${source}`; * @param {Function} options.onSuccess Side effect function to be executed upon success, e.g. { onSuccess: { refresh: true } } * @param {Function} options.onFailure Side effect function to be executed upon failure, e.g. { onFailure: error => notify(error.message) } * - * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded }. + * @returns The current request state. Destructure as { data, total, ids, error, loading, loaded, refetch }. * * @example * @@ -93,6 +93,7 @@ const useGetMatching = ( error, loading, loaded, + refetch, } = useQueryWithStore( { type: 'getList', @@ -143,6 +144,7 @@ const useGetMatching = ( error, loading, loaded, + refetch, }; }; @@ -153,6 +155,7 @@ interface UseGetMatchingResult { error?: any; loading: boolean; loaded: boolean; + refetch: Refetch; } export default useGetMatching; diff --git a/packages/ra-core/src/dataProvider/useGetOne.ts b/packages/ra-core/src/dataProvider/useGetOne.ts index df83de560a6..ff407930d0c 100644 --- a/packages/ra-core/src/dataProvider/useGetOne.ts +++ b/packages/ra-core/src/dataProvider/useGetOne.ts @@ -6,7 +6,7 @@ import { ReduxState, UseDataProviderOptions, } from '../types'; -import { useQueryWithStore } from './useQueryWithStore'; +import { useQueryWithStore, Refetch } from './useQueryWithStore'; /** * Call the dataProvider.getOne() method and return the resolved value @@ -14,9 +14,9 @@ import { useQueryWithStore } from './useQueryWithStore'; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -28,7 +28,7 @@ import { useQueryWithStore } from './useQueryWithStore'; * @param {Function} options.onSuccess Side effect function to be executed upon success, e.g. { onSuccess: { refresh: true } } * @param {Function} options.onFailure Side effect function to be executed upon failure, e.g. { onFailure: error => notify(error.message) } * - * @returns The current request state. Destructure as { data, error, loading, loaded }. + * @returns The current request state. Destructure as { data, error, loading, loaded, refetch }. * * @example * @@ -69,6 +69,7 @@ export type UseGetOneHookValue = { loading: boolean; loaded: boolean; error?: any; + refetch: Refetch; }; export default useGetOne; diff --git a/packages/ra-core/src/dataProvider/useQuery.ts b/packages/ra-core/src/dataProvider/useQuery.ts index 23fab41695e..aee914f6eea 100644 --- a/packages/ra-core/src/dataProvider/useQuery.ts +++ b/packages/ra-core/src/dataProvider/useQuery.ts @@ -13,9 +13,9 @@ import { DataProviderQuery, Refetch } from './useQueryWithStore'; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from response], total: [total from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from response], total: [total from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * @param {Object} query * @param {string} query.type The method called on the data provider, e.g. 'getList', 'getOne'. Can also be a custom method if the dataProvider supports is. @@ -28,7 +28,7 @@ import { DataProviderQuery, Refetch } from './useQueryWithStore'; * @param {Function} options.onFailure Side effect function to be executed upon failure, e.g. (error) => notify(error.message) * @param {boolean} options.withDeclarativeSideEffectsSupport Set to true to support legacy side effects e.g. { onSuccess: { refresh: true } } * - * @returns The current request state. Destructure as { data, total, error, loading, loaded }. + * @returns The current request state. Destructure as { data, total, error, loading, loaded, refetch }. * * @example * diff --git a/packages/ra-core/src/dataProvider/useQueryWithStore.ts b/packages/ra-core/src/dataProvider/useQueryWithStore.ts index 602088c8705..c6bda77cf6f 100644 --- a/packages/ra-core/src/dataProvider/useQueryWithStore.ts +++ b/packages/ra-core/src/dataProvider/useQueryWithStore.ts @@ -70,9 +70,9 @@ const defaultIsDataLoaded = (data: any): boolean => data !== undefined; * * The return value updates according to the request state: * - * - start: { loading: true, loaded: false } - * - success: { data: [data from response], total: [total from response], loading: false, loaded: true } - * - error: { error: [error from response], loading: false, loaded: true } + * - start: { loading: true, loaded: false, refetch } + * - success: { data: [data from response], total: [total from response], loading: false, loaded: true, refetch } + * - error: { error: [error from response], loading: false, loaded: true, refetch } * * This hook will return the cached result when called a second time * with the same parameters, until the response arrives. @@ -90,7 +90,7 @@ const defaultIsDataLoaded = (data: any): boolean => data !== undefined; * @param {Function} totalSelector Redux selector to get the total (optional, only for LIST queries) * @param {Function} isDataLoaded * - * @returns The current request state. Destructure as { data, total, error, loading, loaded }. + * @returns The current request state. Destructure as { data, total, error, loading, loaded, refetch }. * * @example * diff --git a/packages/ra-core/src/types.ts b/packages/ra-core/src/types.ts index 5fd27bff7cc..59869e013f3 100644 --- a/packages/ra-core/src/types.ts +++ b/packages/ra-core/src/types.ts @@ -330,10 +330,7 @@ export interface ReduxState { resources: { [name: string]: { props: ResourceDefinition; - data: { - [key: string]: Record; - [key: number]: Record; - }; + data: RecordMap; list: { cachedRequests?: { ids: Identifier[]; diff --git a/packages/ra-ui-materialui/src/detail/EditView.tsx b/packages/ra-ui-materialui/src/detail/EditView.tsx index 06456c78786..1e3c632995a 100644 --- a/packages/ra-ui-materialui/src/detail/EditView.tsx +++ b/packages/ra-ui-materialui/src/detail/EditView.tsx @@ -190,6 +190,7 @@ const sanitizeRestProps = ({ onSuccessRef = null, options = null, permissions = null, + refetch = null, save = null, saving = null, setOnFailure = null, diff --git a/packages/ra-ui-materialui/src/detail/ShowView.tsx b/packages/ra-ui-materialui/src/detail/ShowView.tsx index 8724d87a0ae..2e99756b747 100644 --- a/packages/ra-ui-materialui/src/detail/ShowView.tsx +++ b/packages/ra-ui-materialui/src/detail/ShowView.tsx @@ -153,6 +153,7 @@ const sanitizeRestProps = ({ location = null, match = null, options = null, + refetch = null, permissions = null, ...rest }) => rest; diff --git a/packages/ra-ui-materialui/src/field/ReferenceField.tsx b/packages/ra-ui-materialui/src/field/ReferenceField.tsx index e22f4c1bc6e..fb98669b97b 100644 --- a/packages/ra-ui-materialui/src/field/ReferenceField.tsx +++ b/packages/ra-ui-materialui/src/field/ReferenceField.tsx @@ -188,6 +188,7 @@ export const ReferenceFieldView: FC = props => { record, reference, referenceRecord, + refetch, resource, resourceLinkPath, source, @@ -229,6 +230,7 @@ export const ReferenceFieldView: FC = props => { classes.link // force color override for Typography components ), record: referenceRecord, + refetch, resource: reference, basePath, translateChoice, diff --git a/packages/ra-ui-materialui/src/list/ListView.tsx b/packages/ra-ui-materialui/src/list/ListView.tsx index a14947c4eb0..d84d8f97ce9 100644 --- a/packages/ra-ui-materialui/src/list/ListView.tsx +++ b/packages/ra-ui-materialui/src/list/ListView.tsx @@ -241,6 +241,7 @@ const sanitizeRestProps: ( page = null, permissions = null, perPage = null, + refetch = null, resource = null, selectedIds = null, setFilters = null,