Skip to content

Add support for offline/local first applications #10545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: next
Choose a base branch
from

Conversation

djhi
Copy link
Collaborator

@djhi djhi commented Feb 24, 2025

Problem

React-admin currently don't handle well the loss of network connectivity: in any demo, if you go to a list page, then simulate being offline using the devtool and try to go to the second page of the same list, it will still display the first page data while the pagination will indeed indicate you are on page 2.

We handled it a bit better by at least showing users a notification when they try to load new data while offline but we can do better and allow for resumable mutations.

Solution

How To Test

  • Open the simple example
  • Use the browser devtool to go offline
  • Click the posts create button and fill it
  • Go back to the posts list after and notice the new item is there
  • Open the devtool and note there is no create call to the dataProvider
  • Use the browser devtool to go online
  • Open the devtool and note there is a create call to the dataProvider

Then:

  • Follow the same steps but click the ?mutationMode=optimistic link first.
  • Follow the same steps but click the ?mutationMode=undoable link first.
  • Follow the same steps but edit a record instead of creating it.
  • Follow the same steps for both edit and create but enter f00bar as the post title to trigger an error once the network is online.

Additional Checks

  • The PR targets master for a bugfix, or next for a feature
  • The PR includes unit tests (if not possible, describe why)
  • The PR includes one or several stories (if not possible, describe why)
  • The documentation is up to date

Also, please make sure to read the contributing guidelines.

EditPostPage.setInputValue(
'input',
'title',
'Lorem Ipsum again{enter}'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because react-query now persist queries and mutations for offline mode, the previous test now leaks into the second (e.g. this post has its title changed to Lorem Ipsum). I tried to configure testIsolation in Cypress but our version is probably too old

@djhi djhi added RFR Ready For Review and removed WIP Work In Progress labels Apr 30, 2025
@djhi
Copy link
Collaborator Author

djhi commented Apr 30, 2025

I can't see a way to test this except e2e. Not sure if we can simulate network loss with cypress though

@slax57
Copy link
Contributor

slax57 commented Apr 30, 2025

@djhi since the simple demo has the react-query DevTools enabled, you can use them to simulate network loss I believe (clicking on the 'Wifi' icon does the job). A bit ugly but it should work!

Copy link
Contributor

@erwanMarmelab erwanMarmelab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Praise: very understandable doc !!

Copy link
Member

@fzaninotto fzaninotto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the change of the ListContext shape affects more components; e.g. useListContextWithProps.

const queryClient = useQueryClient();
const notify = useNotify();

queryClient.setMutationDefaults([], {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like an effect, why do you execute it on render?

queryClient.setMutationDefaults([], {
onSettled(data, error) {
if (error) {
notify(error.message, { type: 'error' });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will result in a doubled mutation with e.g. <Edit>, which already shows an error notification with onError. Also, I don't understand the point of showing directly the error message...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you actually try it? This effect only runs when there are persisted mutations to resume. For instance, create a new post with title f00bar in optimistic mode while offline and click Save and edit then close your browser tab. Reopen the tab, restore the network if needed. You'll see the server error as expected.
However, should you do the same while offline, the above function will not be called

</>
);
export default ({ children }) => {
return (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please keep the simple functional syntax for brevity.

setIsOnline(isOnline);
};
return onlineManager.subscribe(handleChange);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to run this effect every time the app bar renders (which is often) instead of just on mount?

>
<Alert severity="info" role="presentation">
To test offline support, add either{' '}
<Link to="/posts/create?mutationMode=optimistic">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you use a special location state instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this comment

* </PersistQueryClientProvider>
* );
*/
export const addOfflineSupportToQueryClient = ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As explained earlier, can't we create a new Query Client instead? or at least return the modified one.

@@ -8,6 +8,14 @@ export const UPDATE_MANY = 'UPDATE_MANY';
export const DELETE = 'DELETE';
export const DELETE_MANY = 'DELETE_MANY';

export const reactAdminMutations = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"data provider mutations" would be a better name IMHO

@@ -176,7 +176,7 @@ export const useGetList = <
}
: result,
[result]
) as UseQueryResult<RecordType[], Error> & {
) as unknown as UseQueryResult<RecordType[], Error> & {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this?

!Object.keys(filterValues).length &&
// there is an empty page component
empty !== false;
(isPaused && isPlaceholderData) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the rationale of showing the empty page when offline. This gives a wrong information.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we introduce a new offline prop and <Offline> component?

Co-authored-by: Francois Zaninotto <[email protected]>
@djhi
Copy link
Collaborator Author

djhi commented May 7, 2025

I think the change of the ListContext shape affects more components; e.g. useListContextWithProps.

That's the only one left AFAIK

@djhi djhi added WIP Work In Progress and removed RFR Ready For Review labels May 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WIP Work In Progress
Development

Successfully merging this pull request may close these issues.

4 participants