diff --git a/.changeset/loud-hairs-think.md b/.changeset/loud-hairs-think.md new file mode 100644 index 00000000000..7e29ffae5ec --- /dev/null +++ b/.changeset/loud-hairs-think.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": patch +--- + +Fix a bug where calling the `useMutation` `reset` function would point the hook to an outdated `client` reference. diff --git a/.changeset/mighty-monkeys-explain.md b/.changeset/mighty-monkeys-explain.md new file mode 100644 index 00000000000..4629cd11273 --- /dev/null +++ b/.changeset/mighty-monkeys-explain.md @@ -0,0 +1,6 @@ +--- +"@apollo/client": patch +--- + +Prevent writing to a ref in render in `useMutation`. +As a result, you might encounter problems in the future if you call the mutation's `execute` function during render. Please note that this was never supported behavior, and we strongly recommend against it. diff --git a/.size-limits.json b/.size-limits.json index d082ee9265e..4176d307cbb 100644 --- a/.size-limits.json +++ b/.size-limits.json @@ -1,4 +1,4 @@ { - "dist/apollo-client.min.cjs": 39607, + "dist/apollo-client.min.cjs": 39620, "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32821 } diff --git a/src/react/components/__tests__/client/Mutation.test.tsx b/src/react/components/__tests__/client/Mutation.test.tsx index 80c75f99506..b71ba82f7cc 100644 --- a/src/react/components/__tests__/client/Mutation.test.tsx +++ b/src/react/components/__tests__/client/Mutation.test.tsx @@ -1348,7 +1348,7 @@ describe("General Mutation testing", () => { if (count === 0) { expect(result.called).toEqual(false); expect(result.loading).toEqual(false); - createTodo(); + setTimeout(createTodo, 10); } else if (count === 2 && result) { expect(result.data).toEqual(data); setTimeout(() => { @@ -1358,7 +1358,7 @@ describe("General Mutation testing", () => { }); } else if (count === 3) { expect(result.loading).toEqual(false); - createTodo(); + setTimeout(createTodo, 10); } else if (count === 5) { expect(result.data).toEqual(data3); } diff --git a/src/react/hoc/__tests__/mutations/lifecycle.test.tsx b/src/react/hoc/__tests__/mutations/lifecycle.test.tsx index f74895780a3..f6ff711e48d 100644 --- a/src/react/hoc/__tests__/mutations/lifecycle.test.tsx +++ b/src/react/hoc/__tests__/mutations/lifecycle.test.tsx @@ -92,7 +92,9 @@ describe("graphql(mutation) lifecycle", () => { class Container extends React.Component> { render() { if (this.props.listId !== 2) return null; - this.props.mutate!().then(() => resolve()); + setTimeout(() => { + this.props.mutate!().then(() => resolve()); + }); return null; } } diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts index 79825f91524..65f0820e015 100644 --- a/src/react/hooks/useMutation.ts +++ b/src/react/hooks/useMutation.ts @@ -99,11 +99,9 @@ export function useMutation< options, }); - // TODO: Trying to assign these in a useEffect or useLayoutEffect breaks - // higher-order components. - { + React.useLayoutEffect(() => { Object.assign(ref.current, { client, options, mutation }); - } + }); const execute = React.useCallback( ( @@ -221,17 +219,22 @@ export function useMutation< const reset = React.useCallback(() => { if (ref.current.isMounted) { - const result = { called: false, loading: false, client }; + const result = { + called: false, + loading: false, + client: ref.current.client, + }; Object.assign(ref.current, { mutationId: 0, result }); setResult(result); } }, []); React.useEffect(() => { - ref.current.isMounted = true; + const current = ref.current; + current.isMounted = true; return () => { - ref.current.isMounted = false; + current.isMounted = false; }; }, []);