Skip to content

Commit

Permalink
Merge pull request #6054 from marmelab/soft-refresh
Browse files Browse the repository at this point in the history
Update refresh strategy to avoid empty page while refreshing
  • Loading branch information
djhi authored Mar 26, 2021
2 parents fe24333 + 7b54575 commit 47f3c5d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 4 deletions.
4 changes: 3 additions & 1 deletion packages/ra-core/src/actions/uiActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ export const REFRESH_VIEW = 'RA/REFRESH_VIEW';

export interface RefreshViewAction {
readonly type: typeof REFRESH_VIEW;
readonly payload: { hard: boolean };
}

export const refreshView = (): RefreshViewAction => ({
export const refreshView = (hard?: boolean): RefreshViewAction => ({
type: REFRESH_VIEW,
payload: { hard },
});

export const SET_AUTOMATIC_REFRESH = 'RA/SET_AUTOMATIC_REFRESH';
Expand Down
36 changes: 36 additions & 0 deletions packages/ra-core/src/dataProvider/useDataProvider.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,42 @@ describe('useDataProvider', () => {
expect(getOne).toBeCalledTimes(2);
});

it('should not use the cache after a hard refresh', async () => {
const getOne = jest.fn(() => {
const validUntil = new Date();
validUntil.setTime(validUntil.getTime() + 1000);
return Promise.resolve({ data: { id: 1 }, validUntil });
});
const dataProvider = { getOne };
const Refresh = () => {
const refresh = useRefresh();
return <button onClick={() => refresh(true)}>refresh</button>;
};
const { getByText, rerender } = renderWithRedux(
<DataProviderContext.Provider value={dataProvider}>
<UseGetOne key="1" />
<Refresh />
</DataProviderContext.Provider>,
{ admin: { resources: { posts: { data: {}, list: {} } } } }
);
// waitFor for the dataProvider to return
await act(async () => await new Promise(r => setTimeout(r)));
// click on the refresh button
expect(getOne).toBeCalledTimes(1);
await act(async () => {
fireEvent.click(getByText('refresh'));
await new Promise(r => setTimeout(r));
});
rerender(
<DataProviderContext.Provider value={dataProvider}>
<UseGetOne key="2" />
</DataProviderContext.Provider>
);
// waitFor for the dataProvider to return
await act(async () => await new Promise(r => setTimeout(r)));
expect(getOne).toBeCalledTimes(2);
});

it('should not use the cache after an update', async () => {
const getOne = jest.fn(() => {
const validUntil = new Date();
Expand Down
16 changes: 14 additions & 2 deletions packages/ra-core/src/reducer/admin/resource/list/cachedRequests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,20 @@ const cachedRequestsReducer: Reducer<State> = (
action
) => {
if (action.type === REFRESH_VIEW) {
// force refresh
return initialState;
if (action.payload?.hard) {
// force refresh
return initialState;
} else {
// remove validity only
const newState = {};
Object.keys(previousState).forEach(key => {
newState[key] = {
...previousState[key],
validity: undefined,
};
});
return newState;
}
}
if (action.meta && action.meta.optimistic) {
if (
Expand Down
14 changes: 13 additions & 1 deletion packages/ra-core/src/sideEffect/useRefresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,27 @@ import { refreshView } from '../actions/uiActions';
/**
* Hook for Refresh Side Effect
*
* Returns a callback that triggers a page refresh. The callback causes a
* version increase, which forces a re-execution all queries based on the
* useDataProvider() hook, and a rerender of all components using the version
* as key.
*
* @param hard If true, the callback empties the cache, too
*
* @example
*
* const refresh = useRefresh();
* // soft refresh
* refresh();
* // hard refresh
* refresh(true)
*/
const useRefresh = () => {
const dispatch = useDispatch();
return useCallback(
(doRefresh = true) => doRefresh && dispatch(refreshView()),
(hard?: boolean) => {
dispatch(refreshView(hard));
},
[dispatch]
);
};
Expand Down

0 comments on commit 47f3c5d

Please sign in to comment.