Skip to content

Commit

Permalink
useMutation also reset internal state on reset (#10931)
Browse files Browse the repository at this point in the history
  • Loading branch information
phryneas committed Nov 10, 2023
1 parent 291aea5 commit e5acf91
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/yellow-fans-move.md
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

`useMutation`: also reset internal state on reset
2 changes: 1 addition & 1 deletion .size-limit.cjs
@@ -1,7 +1,7 @@
const checks = [
{
path: "dist/apollo-client.min.cjs",
limit: "37956",
limit: "37972",
},
{
path: "dist/main.cjs",
Expand Down
87 changes: 86 additions & 1 deletion src/react/hooks/__tests__/useMutation.test.tsx
Expand Up @@ -23,13 +23,14 @@ import {
MockSubscriptionLink,
mockSingleLink,
subscribeAndCount,
MockedResponse,
} from "../../../testing";
import { ApolloProvider } from "../../context";
import { useQuery } from "../useQuery";
import { useMutation } from "../useMutation";
import { BatchHttpLink } from "../../../link/batch-http";
import { FetchResult } from "../../../link/core";
import { spyOnConsole } from "../../../testing/internal";
import { profileHook, spyOnConsole } from "../../../testing/internal";

describe("useMutation Hook", () => {
interface Todo {
Expand Down Expand Up @@ -719,6 +720,90 @@ describe("useMutation Hook", () => {
{ interval: 1 }
);
});

it("resetting while a mutation is running: ensure that the result doesn't end up in the hook", async () => {
const CREATE_TODO_DATA = {
createTodo: {
id: 1,
priority: "Low",
description: "Get milk!",
__typename: "Todo",
},
};

const mocks: MockedResponse[] = [
{
request: {
query: CREATE_TODO_MUTATION,
variables: {
priority: "Low",
description: "Get milk.",
},
},
result: {
data: CREATE_TODO_DATA,
},
delay: 20,
},
];

const ProfiledHook = profileHook(() =>
useMutation<
{ createTodo: Todo },
{ priority: string; description: string }
>(CREATE_TODO_MUTATION)
);

render(<ProfiledHook />, {
wrapper: ({ children }) => (
<MockedProvider mocks={mocks}>{children}</MockedProvider>
),
});

let createTodo: Awaited<ReturnType<typeof ProfiledHook.takeSnapshot>>[0];
let reset: Awaited<
ReturnType<typeof ProfiledHook.takeSnapshot>
>[1]["reset"];

{
const [mutate, result] = await ProfiledHook.takeSnapshot();
createTodo = mutate;
reset = result.reset;
//initial value
expect(result.data).toBe(undefined);
expect(result.loading).toBe(false);
expect(result.called).toBe(false);
}

let fetchResult: any;
act(() => {
fetchResult = createTodo({
variables: { priority: "Low", description: "Get milk." },
});
});

{
const [, result] = await ProfiledHook.takeSnapshot();
// started loading
expect(result.data).toBe(undefined);
expect(result.loading).toBe(true);
expect(result.called).toBe(true);
}

act(() => reset());

{
const [, result] = await ProfiledHook.takeSnapshot();
// reset to initial value
expect(result.data).toBe(undefined);
expect(result.loading).toBe(false);
expect(result.called).toBe(false);
}

expect(await fetchResult).toEqual({ data: CREATE_TODO_DATA });

await expect(ProfiledHook).not.toRerender();
});
});

describe("Callbacks", () => {
Expand Down
4 changes: 3 additions & 1 deletion src/react/hooks/useMutation.ts
Expand Up @@ -164,7 +164,9 @@ export function useMutation<

const reset = React.useCallback(() => {
if (ref.current.isMounted) {
setResult({ called: false, loading: false, client });
const result = { called: false, loading: false, client };
Object.assign(ref.current, { mutationId: 0, result });
setResult(result);
}
}, []);

Expand Down

0 comments on commit e5acf91

Please sign in to comment.