('pessimistic');
+ const [update, { isPending }] = useUpdate('posts', undefined, {
+ mutationMode,
+ onSuccess: () => setSuccess('success'),
+ });
+ const handleClick = () => {
+ update('posts', {
+ id: 1,
+ data: { title: `Hello World ${counter}` },
+ });
+ };
+ return (
+ <>
+
+ - title
+ - {data?.title}
+ - author
+ - {data?.author}
+
+
+
+
+
+
+
+
+
+
+ {success && {success}
}
+ {isMutating !== 0 && mutating
}
+ >
+ );
+};
+
+export const UndefinedValues = () => {
+ const data = { id: 1, title: 'Hello' };
+ const dataProvider = {
+ getOne: async () => ({ data }),
+ update: () => new Promise(() => {}), // never resolve to see only optimistic update
+ } as any;
+ return (
+
+
+
+ );
+};
+
+const UndefinedValuesCore = () => {
+ const { data } = useGetOne('posts', { id: 1 });
+ const [update, { isPending }] = useUpdate();
+ const handleClick = () => {
+ update(
+ 'posts',
+ { id: 1, data: { id: undefined, title: 'world' } },
+ { mutationMode: 'optimistic' }
+ );
+ };
+ return (
+ <>
+ {JSON.stringify(data)}
+
+
+
+ >
+ );
+};
+
+export const ErrorCase = ({ timeout = 1000 }) => {
+ const posts = [{ id: 1, title: 'Hello', author: 'John Doe' }];
+ const dataProvider = {
+ getOne: (resource, params) => {
+ return Promise.resolve({
+ data: posts.find(p => p.id === params.id),
+ });
+ },
+ update: () => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ reject(new Error('something went wrong'));
+ }, timeout);
+ });
+ },
+ } as any;
+ return (
+
+
+
+ );
+};
+
+const ErrorCore = () => {
+ const isMutating = useIsMutating();
+ const [success, setSuccess] = useState();
+ const [error, setError] = useState();
+ const { data, refetch } = useGetOne('posts', { id: 1 });
+ const [update, { isPending }] = useUpdate();
+ const handleClick = () => {
+ update(
+ 'posts',
+ {
+ id: 1,
+ data: { title: 'Hello World' },
+ },
+ {
+ mutationMode: 'optimistic',
+ onSuccess: () => setSuccess('success'),
+ onError: e => {
+ setError(e);
+ setSuccess('');
+ },
+ }
+ );
+ };
+ return (
+ <>
+
+ - title
+ - {data?.title}
+ - author
+ - {data?.author}
+
+
+
+
+
+
+ {success && {success}
}
+ {error && {error.message}
}
+ {isMutating !== 0 && mutating
}
+ >
+ );
+};
+
+export const WithMiddlewaresSuccess = ({ timeout = 1000 }) => {
+ const posts = [{ id: 1, title: 'Hello', author: 'John Doe' }];
+ const dataProvider = {
+ getOne: (resource, params) => {
+ return Promise.resolve({
+ data: posts.find(p => p.id === params.id),
+ });
+ },
+ update: (resource, params) => {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ const post = posts.find(p => p.id === params.id);
+ if (post) {
+ post.title = params.data.title;
+ }
+ resolve({ data: post });
+ }, timeout);
+ });
+ },
+ } as any;
+ return (
+
+
+
+ );
+};
+
+const WithMiddlewaresSuccessCore = () => {
+ const isMutating = useIsMutating();
+ const [success, setSuccess] = useState();
+ const { data, refetch } = useGetOne('posts', { id: 1 });
+ const [update, { isPending }] = useUpdate(
+ 'posts',
+ {
+ id: 1,
+ data: { title: 'Hello World' },
+ },
+ {
+ mutationMode: 'optimistic',
+ // @ts-ignore
+ getMutateWithMiddlewares: mutate => async (resource, params) => {
+ return mutate(resource, {
+ ...params,
+ data: { title: `${params.data.title} from middleware` },
+ });
+ },
+ }
+ );
+ const handleClick = () => {
+ update(
+ 'posts',
+ {
+ id: 1,
+ data: { title: 'Hello World' },
+ },
+ {
+ onSuccess: () => setSuccess('success'),
+ }
+ );
+ };
+ return (
+ <>
+
+ - title
+ - {data?.title}
+ - author
+ - {data?.author}
+
+
+
+
+
+
+ {success && {success}
}
+ {isMutating !== 0 && mutating
}
+ >
+ );
+};
+
+export const WithMiddlewaresError = ({ timeout = 1000 }) => {
+ const posts = [{ id: 1, title: 'Hello', author: 'John Doe' }];
+ const dataProvider = {
+ getOne: (resource, params) => {
+ return Promise.resolve({
+ data: posts.find(p => p.id === params.id),
+ });
+ },
+ update: () => {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ reject(new Error('something went wrong'));
+ }, timeout);
+ });
+ },
+ } as any;
+ return (
+
+
+
+ );
+};
+
+const WithMiddlewaresErrorCore = () => {
+ const isMutating = useIsMutating();
+ const [success, setSuccess] = useState();
+ const [error, setError] = useState();
+ const { data, refetch } = useGetOne('posts', { id: 1 });
+ const [update, { isPending }] = useUpdate(
+ 'posts',
+ {
+ id: 1,
+ data: { title: 'Hello World' },
+ },
+ {
+ mutationMode: 'optimistic',
+ // @ts-ignore
+ mutateWithMiddlewares: mutate => async (resource, params) => {
+ return mutate(resource, {
+ ...params,
+ data: { title: `${params.data.title} from middleware` },
+ });
+ },
+ }
+ );
+ const handleClick = () => {
+ setError(undefined);
+ update(
+ 'posts',
+ {
+ id: 1,
+ data: { title: 'Hello World' },
+ },
+ {
+ onSuccess: () => setSuccess('success'),
+ onError: e => {
+ setError(e);
+ setSuccess('');
+ },
+ }
+ );
+ };
+ return (
+ <>
+
+ - title
+ - {data?.title}
+ - author
+ - {data?.author}
+
+
+
+
+
+
+ {error && {error.message}
}
+ {success && {success}
}
+ {isMutating !== 0 && mutating
}
+ >
+ );
+};
diff --git a/packages/ra-core/src/dataProvider/useUpdate.ts b/packages/ra-core/src/dataProvider/useUpdate.ts
index 9e3e53c21e8..3914eb1bca3 100644
--- a/packages/ra-core/src/dataProvider/useUpdate.ts
+++ b/packages/ra-core/src/dataProvider/useUpdate.ts
@@ -1,4 +1,4 @@
-import { useMemo, useRef } from 'react';
+import { useEffect, useMemo, useRef } from 'react';
import {
useMutation,
useQueryClient,
@@ -99,6 +99,9 @@ export const useUpdate = (
...mutationOptions
} = options;
const mode = useRef(mutationMode);
+ useEffect(() => {
+ mode.current = mutationMode;
+ }, [mutationMode]);
const paramsRef = useRef>>(params);
const snapshot = useRef([]);
// Ref that stores the mutation with middlewares to avoid losing them if the calling component is unmounted