Skip to content

Commit 0b14a48

Browse files
author
React-Admin CI
committed
[Doc] Update RA Core EE documentation
1 parent 6364076 commit 0b14a48

File tree

5 files changed

+720
-0
lines changed

5 files changed

+720
-0
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
title: "<AutoPersistInStoreBase>"
3+
---
4+
5+
A component that saves a form data in the store on change and reapplies it on mount.
6+
It's ideal to ensure users don't lose their already filled data in an edit or a create form when they navigate to another page.
7+
8+
This component prevents data loss in forms by automatically saving the form data in the store when users update it. When users return to the page, it reapplies the saved data to the form.
9+
10+
<video controls autoplay playsinline muted loop>
11+
<source src="https://react-admin-ee.marmelab.com/assets/AutoPersistInStore.mp4" type="video/mp4"/>
12+
Your browser does not support the video tag.
13+
</video>
14+
15+
The temporary form data is only saved when the user navigates away from the page, and it is removed when the user submits the form or closes the tab. Users can opt out of the prefilling by clicking the "Cancel" button in the notification.
16+
17+
Saved data is not sent to the server. It is only persisted using the [store](https://marmelab.com/ra-core/store/) and is removed when the user logs out.
18+
19+
### Usage
20+
21+
Add `<AutoPersistInStoreBase>` inside a react-admin form:
22+
23+
```tsx
24+
import { AutoPersistInStoreBase, useAutoPersistInStoreContext } from '@react-admin/ra-core-ee';
25+
import { EditBase, Form, Translate, useEvent, useCloseNotification } from 'ra-core';
26+
import { Button, TextInput } from 'my-react-admin-ui-library';
27+
28+
const PostEdit = () => (
29+
<EditBase>
30+
<Form>
31+
<TextInput source="title" />
32+
<TextInput source="teaser" />
33+
<AutoPersistInStoreBase notification={<AutoPersistNotification />} />
34+
</Form>
35+
</EditBase>
36+
);
37+
38+
const AutoPersistNotification = () => {
39+
const closeNotification = useCloseNotification();
40+
const { reset } = useAutoPersistInStoreContext();
41+
42+
const cancel = useEvent((event: React.MouseEvent) => {
43+
event.preventDefault();
44+
reset();
45+
closeNotification();
46+
});
47+
48+
return (
49+
<div>
50+
<Translate i18nKey="ra-form-layout.auto_persist_in_store.applied_changes" />
51+
<Button label="ra.action.cancel" onClick={cancel} />
52+
</div>
53+
);
54+
};
55+
```
56+
57+
The component will automatically save the form data in the store on change and reapply it when the form is mounted again.
58+
59+
It works both on create and edit forms.
60+
61+
### Props
62+
63+
| Prop | Required | Type | Default | Description |
64+
| --------------------- | -------- | ----------- | ------------------------------------ | ------------------------------------ |
65+
| `notification` | Required | `ReactNode` | - | A Notification element. |
66+
| `getStoreKey` | - | `function` | - | Function to use your own store key. |
67+
68+
### `getStoreKey`
69+
70+
To save the current form data in the [store](https://marmelab.com/ra-core/usestorecontext/), `<AutoPersistInStore>` uses the following store key:
71+
72+
`ra-persist-[RESOURCE_NAME]-[RECORD_ID]`
73+
74+
For example, if you are editing a `posts` resource with the ID `123`, the store key will be: `ra-persist-posts-123`. In case of a create form, the record ID is replaced by `"create"`
75+
76+
You can override this key by passing a custom function as the `getStoreKey` prop. It expects two parameters:
77+
78+
- `resource`: The current resource.
79+
- `record`: The current record if you are in an [edit context](https://marmelab.com/ra-core/useeditcontext/).
80+
81+
```tsx
82+
<AutoPersistInStoreBase
83+
getStoreKey={
84+
(resource: ResourceContextValue, record: RaRecord<Identifier> | undefined) =>
85+
`my-custom-persist-key-${resource}-${record && record.hasOwnProperty('id') ? record.id : 'create'}`
86+
}
87+
notification={<AutoPersistNotification />}
88+
/>
89+
```
90+
91+
### `notification`
92+
93+
When `<AutoPersistInStoreBase>` component applies the changes from the store to a form, react-admin informs users with a notification.
94+
The notification element provided will be passed to the `notify` function of the [`useNotify` hook](https://marmelab.com/ra-core/usenotify/).
95+
96+
```tsx
97+
import { AutoPersistInStoreBase, useAutoPersistInStoreContext } from '@react-admin/ra-core-ee';
98+
import { EditBase, Form, Translate, useEvent, useCloseNotification } from 'ra-core';
99+
import { Button, TextInput } from 'my-react-admin-ui-library';
100+
101+
const PostEdit = () => (
102+
<EditBase>
103+
<Form>
104+
<TextInput source="title" />
105+
<TextInput source="teaser" />
106+
<AutoPersistInStoreBase notification={<AutoPersistNotification />} />
107+
</Form>
108+
</EditBase>
109+
);
110+
111+
const AutoPersistNotification = () => {
112+
const closeNotification = useCloseNotification();
113+
const { reset } = useAutoPersistInStoreContext();
114+
115+
// Let users cancel the persisted changes and reset the form to its default values
116+
const cancel = useEvent((event: React.MouseEvent) => {
117+
event.preventDefault();
118+
reset();
119+
closeNotification();
120+
});
121+
122+
return (
123+
<div>
124+
<Translate i18nKey="ra-form-layout.auto_persist_in_store.applied_changes" />
125+
<Button label="ra.action.cancel" onClick={cancel} />
126+
</div>
127+
);
128+
};
129+
```
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
---
2+
title: "<AutoSaveBase>"
3+
---
4+
5+
A component that enables autosaving of the form. It's ideal for long data entry tasks, and reduces the risk of data loss.
6+
7+
<video controls autoplay playsinline muted loop>
8+
<source src="https://react-admin-ee.marmelab.com/assets/AutoSave.mp4" type="video/mp4"/>
9+
Your browser does not support the video tag.
10+
</video>
11+
12+
## Usage
13+
14+
Put `<AutoSaveBase>` inside a form built with ra-core [`<Form>`](https://marmelab.com/ra-core/form/):
15+
16+
```tsx
17+
import { AutoSaveBase } from '@react-admin/ra-core-ee';
18+
import { EditBase, Form } from 'ra-core';
19+
import { TextInput } from 'my-react-admin-ui-library';
20+
21+
const PostEdit = () => (
22+
<EditBase mutationMode="optimistic">
23+
<Form resetOptions={{ keepDirtyValues: true }}>
24+
<TextInput source="title" />
25+
<TextInput source="teaser" />
26+
<button type="submit">Save</button>
27+
<AutoSaveBase
28+
render={({ error, isSaving, lastSaveAt }) => {
29+
if (error) {
30+
return <span>Error: {error}</span>;
31+
}
32+
if (isSaving) {
33+
return <span>Saving...</span>;
34+
}
35+
if (lastSaveAt) {
36+
return (
37+
<span>
38+
Last saved at{' '}
39+
{new Intl.DateTimeFormat(undefined, {
40+
hour: '2-digit',
41+
minute: '2-digit',
42+
second: '2-digit',
43+
}).format(new Date(lastSaveAt))}
44+
</span>
45+
);
46+
}
47+
}}
48+
/>
49+
</Form>
50+
</EditBase>
51+
);
52+
```
53+
54+
The app will save the current form values after 3 seconds of inactivity.
55+
56+
`<AutoSaveBase>` imposes a few limitations:
57+
58+
- You must set the `<Form resetOptions>` prop to `{ keepDirtyValues: true }`. If you forget that prop, any change entered by the end user after the autosave but before its acknowledgement by the server will be lost.
59+
- In an `<EditBase>` page, you must set [`mutationMode`](https://marmelab.com/ra-core/editbase/#mutationmode) to `pessimistic` or `optimistic` (`<AutoSaveBase>` doesn't work with the default `mutationMode="undoable"`).
60+
- You can't use `<Form warnWhenUnsavedChanges>` with this component. `<AutoSaveBase>` implements its own similar mechanism, and it's enabled by default. You can disable it with the [`disableWarnWhenUnsavedChanges` prop](#disablewarnwhenunsavedchanges).
61+
- It requires that you use a Data Router. This is the default for react-admin apps, but if you're using a custom router, you may need to adjust your configuration. Check the react-router documentation about [Using a Data Router with react-router v6](https://reactrouter.com/6.22.3/routers/picking-a-router) or [Using a Data Router with react-router v7](https://reactrouter.com/7.2.0/start/framework/custom).
62+
63+
## Props
64+
65+
| Prop | Required | Type | Default | Description |
66+
| ------------------------------- | -------- | --------------- | --------- | -------------------------------------------------------------------------- |
67+
| `children` | - | Element | | The content to display by leveraging `AutoSaveContext` |
68+
| `render` | - | Function | | A function to render the content. |
69+
| `debounce` | - | number | 3000 (3s) | The interval in milliseconds between two autosaves. |
70+
| `onSuccess` | - | function | | A callback to call when the save request succeeds. |
71+
| `onError` | - | function | | A callback to call when the save request fails. |
72+
| `transform` | - | function | | A function to transform the data before saving. |
73+
| `disableWarnWhenUnsavedChanges` | - | boolean | false | A boolean indicating whether users should be warned when they close the browser tab or navigate away from the application if they have unsaved changes. |
74+
75+
## `children`
76+
77+
You can pass a children to `<AutoSaveBase>` and leverage its `AutoSaveContext` with the `useAutoSaveContext` hook:
78+
79+
```tsx
80+
import { AutoSaveBase, useAutoSaveContext } from '@react-admin/ra-core-ee';
81+
import { EditBase, Form } from 'ra-core';
82+
import { TextInput } from 'my-react-admin-ui-library';
83+
84+
const AutoSaveContent = () => {
85+
const { error, isSaving, lastSaveAt } = useAutoSaveContext();
86+
87+
if (error) {
88+
return <span>Error: {error}</span>;
89+
}
90+
if (isSaving) {
91+
return <span>Saving...</span>;
92+
}
93+
if (lastSaveAt) {
94+
return (
95+
<span>
96+
Last saved at{' '}
97+
{new Intl.DateTimeFormat(undefined, {
98+
hour: '2-digit',
99+
minute: '2-digit',
100+
second: '2-digit',
101+
}).format(new Date(lastSaveAt))}
102+
</span>
103+
);
104+
}
105+
106+
return null;
107+
}
108+
109+
const PostEdit = () => (
110+
<EditBase mutationMode="optimistic">
111+
<Form resetOptions={{ keepDirtyValues: true }}>
112+
<TextInput source="title" />
113+
<TextInput source="teaser" />
114+
<button type="submit">Save</button>
115+
<AutoSaveBase>
116+
<AutoSaveContent />
117+
</AutoSaveBase>
118+
</Form>
119+
</EditBase>
120+
);
121+
```
122+
123+
## `debounce`
124+
125+
The interval in milliseconds between two autosaves. Defaults to 3000 (3s).
126+
127+
```tsx
128+
<AutoSaveBase debounce={5000} />
129+
```
130+
131+
## `onSuccess`
132+
133+
A callback to call when the save request succeeds.
134+
135+
```tsx
136+
const [lastSave, setLastSave] = useState();
137+
138+
<AutoSaveBase
139+
onSuccess={() => setLastSave(new Date())}
140+
/>
141+
```
142+
143+
## `onError`
144+
145+
A callback to call when the save request fails.
146+
147+
```tsx
148+
const [error, setError] = useState();
149+
150+
<AutoSaveBase
151+
onError={error => setError(error)}
152+
/>
153+
```
154+
155+
## `transform`
156+
157+
A function to transform the data before saving.
158+
159+
```tsx
160+
<AutoSaveBase
161+
transform={data => ({
162+
...data,
163+
fullName: `${data.firstName} ${data.lastName}`
164+
})}
165+
/>
166+
```
167+
168+
## `disableWarnWhenUnsavedChanges`
169+
170+
A boolean indicating whether users should be warned when they close the browser tab or navigate away from the application if they have unsaved changes.
171+
172+
```tsx
173+
<AutoSaveBase disableWarnWhenUnsavedChanges />
174+
```
175+
176+
## `render`
177+
178+
You can pass a `render` prop instead of [`children`](#children) to render a UI for the auto save feature:
179+
180+
```tsx
181+
import { AutoSaveBase } from '@react-admin/ra-core-ee';
182+
183+
const AutoSave = () => (
184+
<AutoSaveBase
185+
render={({ error, isSaving, lastSaveAt }) => {
186+
if (error) {
187+
return <span>Error: {error}</span>;
188+
}
189+
if (isSaving) {
190+
return <span>Saving...</span>;
191+
}
192+
if (lastSaveAt) {
193+
return (
194+
<span>
195+
Last saved at{' '}
196+
{new Intl.DateTimeFormat(undefined, {
197+
hour: '2-digit',
198+
minute: '2-digit',
199+
second: '2-digit',
200+
}).format(new Date(lastSaveAt))}
201+
</span>
202+
);
203+
}
204+
}}
205+
/>
206+
);
207+
```

0 commit comments

Comments
 (0)