Skip to content

Commit 4865cb6

Browse files
authored
Merge pull request #10882 from marmelab/fix-edit-controller-meta
Fix edit controller meta
2 parents 6833126 + 9e85b50 commit 4865cb6

File tree

4 files changed

+79
-6
lines changed

4 files changed

+79
-6
lines changed

docs/Edit.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ const PostEdit = () => (
413413

414414
{% endraw %}
415415

416+
**Warning**: If you set `mutationOptions` meta without [redirecting](#redirect), make sure that [`queryOptions`](#queryoptions) meta is the same, or you will have data update issues.
417+
416418
You can also use `mutationOptions` to override success or error side effects, by setting the `mutationOptions` prop. Refer to the [useMutation documentation](https://tanstack.com/query/v5/docs/react/reference/useMutation) in the react-query website for a list of the possible options.
417419

418420
Let's see an example with the success side effect. By default, when the save action succeeds, react-admin shows a notification, and redirects to the list page. You can override this behavior and pass custom success side effects by providing a `mutationOptions` prop with an `onSuccess` key:
@@ -561,6 +563,8 @@ export const PostShow = () => (
561563

562564
{% endraw %}
563565

566+
**Warning**: If you set `queryOptions` meta without [redirecting](#redirect), make sure that [`mutationOptions`](#mutationoptions) meta is the same, or you will have data update issues.
567+
564568
You can also use `queryOptions` to force a refetch on reconnect:
565569

566570
{% raw %}
@@ -596,7 +600,9 @@ const PostEdit = () => (
596600
);
597601
```
598602

599-
Note that the `redirect` prop is ignored if you set [the `mutationOptions` prop](#mutationoptions). See that prop for how to set a different redirection path in that case.
603+
Note that the `redirect` prop is ignored if you set an `onSuccess` callback of [the `mutationOptions` prop](#mutationoptions). See that prop for how to set a different redirection path in that case.
604+
605+
**Warning**: If you set [`queryOptions`](#queryoptions) meta without redirecting, make sure that [`mutationOptions`](#mutationoptions) meta is the same, or you will have data update issues.
600606

601607
## `render`
602608

packages/ra-core/src/controller/edit/useEditController.spec.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ import {
2828
CanAccess,
2929
DisableAuthentication,
3030
} from './useEditController.security.stories';
31-
import { EncodedId } from './useEditController.stories';
31+
import {
32+
EncodedId,
33+
WarningLogWithDifferentMeta,
34+
} from './useEditController.stories';
3235

3336
const Confirm = () => {
3437
const takeMutation = useTakeUndoableMutation();
@@ -50,6 +53,10 @@ describe('useEditController', () => {
5053
resource: 'posts',
5154
};
5255

56+
afterEach(() => {
57+
jest.restoreAllMocks();
58+
});
59+
5360
it('should call the dataProvider.getOne() function on mount', async () => {
5461
const getOne = jest
5562
.fn()
@@ -217,6 +224,16 @@ describe('useEditController', () => {
217224
});
218225
});
219226

227+
it('should emit a warning when providing a different meta in query options and mutation options without redirecting', async () => {
228+
const warnFn = jest.spyOn(console, 'warn').mockImplementation(() => {});
229+
230+
render(<WarningLogWithDifferentMeta />);
231+
232+
expect(warnFn).toHaveBeenCalledWith(
233+
'When not redirecting after editing, query meta and mutation meta should be the same, or you will have data update issues.'
234+
);
235+
});
236+
220237
it('should call the dataProvider.update() function on save', async () => {
221238
const update = jest
222239
.fn()
@@ -261,6 +278,7 @@ describe('useEditController', () => {
261278
});
262279

263280
it('should return an undoable save callback by default', async () => {
281+
window.confirm = jest.fn().mockReturnValue(true);
264282
let post = { id: 12, test: 'previous' };
265283
const update = jest
266284
.fn()
@@ -293,7 +311,7 @@ describe('useEditController', () => {
293311
await waitFor(() => {
294312
screen.getByText('previous');
295313
});
296-
screen.getByLabelText('save').click();
314+
fireEvent.click(screen.getByLabelText('save'));
297315
await waitFor(() => {
298316
screen.getByText('updated');
299317
});
@@ -302,7 +320,7 @@ describe('useEditController', () => {
302320
data: { test: 'updated' },
303321
previousData: { id: 12, test: 'previous' },
304322
});
305-
screen.getByLabelText('confirm').click();
323+
fireEvent.click(screen.getByLabelText('confirm'));
306324
await waitFor(() => {
307325
screen.getByText('updated');
308326
});
@@ -336,8 +354,7 @@ describe('useEditController', () => {
336354
</EditController>
337355
</CoreAdminContext>
338356
);
339-
await new Promise(resolve => setTimeout(resolve, 10));
340-
screen.getByText('{"id":12}');
357+
await screen.findByText('{"id":12}');
341358
await act(async () => saveCallback({ foo: 'bar' }));
342359
await screen.findByText('{"id":12,"foo":"bar"}');
343360
expect(update).toHaveBeenCalledWith('posts', {

packages/ra-core/src/controller/edit/useEditController.stories.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,40 @@ export const EncodedIdWithPercentage = ({
7575
);
7676
};
7777

78+
export const WarningLogWithDifferentMeta = () => (
79+
<TestMemoryRouter initialEntries={['/posts/5']}>
80+
<CoreAdminContext
81+
dataProvider={testDataProvider({
82+
getOne: (_resource, { id }) =>
83+
Promise.resolve({
84+
data: { id, title: 'hello' } as any,
85+
}),
86+
})}
87+
>
88+
<Routes>
89+
<Route
90+
path="/posts/:id"
91+
element={
92+
<EditController
93+
resource="posts"
94+
queryOptions={{ meta: { foo: 'bar' } }}
95+
redirect={false}
96+
>
97+
{({ record }) => (
98+
<>
99+
<LocationInspector />
100+
<p>Id: {record && record.id}</p>
101+
<p>Title: {record && record.title}</p>
102+
</>
103+
)}
104+
</EditController>
105+
}
106+
/>
107+
</Routes>
108+
</CoreAdminContext>
109+
</TestMemoryRouter>
110+
);
111+
78112
const LocationInspector = () => {
79113
const location = useLocation();
80114
return (

packages/ra-core/src/controller/edit/useEditController.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,25 @@ export const useEditController = <
8989
);
9090
}
9191
const id = propsId ?? routeId;
92+
9293
const { meta: queryMeta, ...otherQueryOptions } = queryOptions;
9394
const {
9495
meta: mutationMeta,
9596
onSuccess,
9697
onError,
9798
...otherMutationOptions
9899
} = mutationOptions;
100+
101+
if (
102+
(queryMeta || mutationMeta) &&
103+
JSON.stringify(queryMeta) !== JSON.stringify(mutationMeta) &&
104+
redirectTo === false
105+
) {
106+
console.warn(
107+
'When not redirecting after editing, query meta and mutation meta should be the same, or you will have data update issues.'
108+
);
109+
}
110+
99111
const {
100112
registerMutationMiddleware,
101113
getMutateWithMiddlewares,
@@ -302,6 +314,7 @@ export interface EditControllerProps<
302314
redirect?: RedirectionSideEffect;
303315
resource?: string;
304316
transform?: TransformData;
317+
305318
[key: string]: any;
306319
}
307320

@@ -322,6 +335,7 @@ export interface EditControllerLoadingResult<RecordType extends RaRecord = any>
322335
error: null;
323336
isPending: true;
324337
}
338+
325339
export interface EditControllerLoadingErrorResult<
326340
RecordType extends RaRecord = any,
327341
TError = Error,
@@ -330,6 +344,7 @@ export interface EditControllerLoadingErrorResult<
330344
error: TError;
331345
isPending: false;
332346
}
347+
333348
export interface EditControllerRefetchErrorResult<
334349
RecordType extends RaRecord = any,
335350
TError = Error,
@@ -338,6 +353,7 @@ export interface EditControllerRefetchErrorResult<
338353
error: TError;
339354
isPending: false;
340355
}
356+
341357
export interface EditControllerSuccessResult<RecordType extends RaRecord = any>
342358
extends EditControllerBaseResult<RecordType> {
343359
record: RecordType;

0 commit comments

Comments
 (0)