Skip to content

Commit

Permalink
Call nextFetchPolicy with "variables-changed" even if there is a `f…
Browse files Browse the repository at this point in the history
…etchPolicy` option specified. (#11626)

* Call `nextFetchPolicy` with "variables-changed" even if there is a `fetchPolicy` specified.
fixes #11365

* update size-limits

* remove `.only`

* Clean up Prettier, Size-limit, and Api-Extractor

* use `mockFetchQuery` helper in test

* fix detail in test-tsconfig.json

---------

Co-authored-by: phryneas <[email protected]>
  • Loading branch information
phryneas and phryneas committed Jul 8, 2024
1 parent 96422ce commit 228429a
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/tasty-chairs-dress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Call `nextFetchPolicy` with "variables-changed" even if there is a `fetchPolicy` specified. (fixes #11365)
4 changes: 2 additions & 2 deletions .size-limits.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"dist/apollo-client.min.cjs": 39906,
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32896
"dist/apollo-client.min.cjs": 39924,
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32903
}
5 changes: 4 additions & 1 deletion src/core/ObservableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,10 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
options.fetchPolicy !== "standby" &&
// If we're changing the fetchPolicy anyway, don't try to change it here
// using applyNextFetchPolicy. The explicit options.fetchPolicy wins.
options.fetchPolicy === oldFetchPolicy
(options.fetchPolicy === oldFetchPolicy ||
// A `nextFetchPolicy` function has even higher priority, though,
// so in that case `applyNextFetchPolicy` must be called.
typeof options.nextFetchPolicy === "function")
) {
this.applyNextFetchPolicy("variables-changed", options);
if (newNetworkStatus === void 0) {
Expand Down
127 changes: 127 additions & 0 deletions src/react/hooks/__tests__/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
OperationVariables,
TypedDocumentNode,
WatchQueryFetchPolicy,
WatchQueryOptions,
} from "../../../core";
import { InMemoryCache } from "../../../cache";
import { ApolloProvider } from "../../context";
Expand All @@ -36,6 +37,7 @@ import {
} from "../../../testing/internal";
import { useApolloClient } from "../useApolloClient";
import { useLazyQuery } from "../useLazyQuery";
import { mockFetchQuery } from "../../../core/__tests__/ObservableQuery";

const IS_REACT_17 = React.version.startsWith("17");

Expand Down Expand Up @@ -7071,6 +7073,131 @@ describe("useQuery Hook", () => {

expect(reasons).toEqual(["variables-changed", "after-fetch"]);
});

it("should prioritize a `nextFetchPolicy` function over a `fetchPolicy` option when changing variables", async () => {
const query = gql`
{
hello
}
`;
const link = new MockLink([
{
request: { query, variables: { id: 1 } },
result: { data: { hello: "from link" } },
delay: 10,
},
{
request: { query, variables: { id: 2 } },
result: { data: { hello: "from link2" } },
delay: 10,
},
]);

const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});

const mocks = mockFetchQuery(client["queryManager"]);

const expectQueryTriggered = (
nth: number,
fetchPolicy: WatchQueryFetchPolicy
) => {
expect(mocks.fetchQueryByPolicy).toHaveBeenCalledTimes(nth);
expect(mocks.fetchQueryByPolicy).toHaveBeenNthCalledWith(
nth,
expect.anything(),
expect.objectContaining({ fetchPolicy }),
expect.any(Number)
);
};
let nextFetchPolicy: WatchQueryOptions<
OperationVariables,
any
>["nextFetchPolicy"] = (_, context) => {
if (context.reason === "variables-changed") {
return "cache-and-network";
} else if (context.reason === "after-fetch") {
return "cache-only";
}
throw new Error("should never happen");
};
nextFetchPolicy = jest.fn(nextFetchPolicy);

const { result, rerender } = renderHook<
QueryResult,
{
variables: { id: number };
}
>(
({ variables }) =>
useQuery(query, {
fetchPolicy: "network-only",
variables,
notifyOnNetworkStatusChange: true,
nextFetchPolicy,
}),
{
initialProps: {
variables: { id: 1 },
},
wrapper: ({ children }) => (
<ApolloProvider client={client}>{children}</ApolloProvider>
),
}
);
// first network request triggers with initial fetchPolicy
expectQueryTriggered(1, "network-only");

await waitFor(() => {
expect(result.current.networkStatus).toBe(NetworkStatus.ready);
});

expect(nextFetchPolicy).toHaveBeenCalledTimes(1);
expect(nextFetchPolicy).toHaveBeenNthCalledWith(
1,
"network-only",
expect.objectContaining({
reason: "after-fetch",
})
);
// `nextFetchPolicy(..., {reason: "after-fetch"})` changed it to
// cache-only
expect(result.current.observable.options.fetchPolicy).toBe("cache-only");

rerender({
variables: { id: 2 },
});

expect(nextFetchPolicy).toHaveBeenNthCalledWith(
2,
// has been reset to the initial `fetchPolicy` of "network-only" because
// we changed variables, then `nextFetchPolicy` is called
"network-only",
expect.objectContaining({
reason: "variables-changed",
})
);
// the return value of `nextFetchPolicy(..., {reason: "variables-changed"})`
expectQueryTriggered(2, "cache-and-network");

await waitFor(() => {
expect(result.current.networkStatus).toBe(NetworkStatus.ready);
});

expect(nextFetchPolicy).toHaveBeenCalledTimes(3);
expect(nextFetchPolicy).toHaveBeenNthCalledWith(
3,
"cache-and-network",
expect.objectContaining({
reason: "after-fetch",
})
);
// `nextFetchPolicy(..., {reason: "after-fetch"})` changed it to
// cache-only
expect(result.current.observable.options.fetchPolicy).toBe("cache-only");
});
});

describe("Missing Fields", () => {
Expand Down
2 changes: 2 additions & 0 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
{
"compilerOptions": {
"noEmit": true,
"declaration": false,
"declarationMap": false,
"lib": ["es2015", "esnext.asynciterable", "ES2021.WeakRef"],
"types": ["jest", "node", "./testing/matchers/index.d.ts"]
},
Expand Down

0 comments on commit 228429a

Please sign in to comment.