Skip to content

Commit 228429a

Browse files
authored
Call nextFetchPolicy with "variables-changed" even if there is a fetchPolicy 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]>
1 parent 96422ce commit 228429a

File tree

5 files changed

+140
-3
lines changed

5 files changed

+140
-3
lines changed

Diff for: .changeset/tasty-chairs-dress.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": patch
3+
---
4+
5+
Call `nextFetchPolicy` with "variables-changed" even if there is a `fetchPolicy` specified. (fixes #11365)

Diff for: .size-limits.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"dist/apollo-client.min.cjs": 39906,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32896
2+
"dist/apollo-client.min.cjs": 39924,
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32903
44
}

Diff for: src/core/ObservableQuery.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,10 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
910910
options.fetchPolicy !== "standby" &&
911911
// If we're changing the fetchPolicy anyway, don't try to change it here
912912
// using applyNextFetchPolicy. The explicit options.fetchPolicy wins.
913-
options.fetchPolicy === oldFetchPolicy
913+
(options.fetchPolicy === oldFetchPolicy ||
914+
// A `nextFetchPolicy` function has even higher priority, though,
915+
// so in that case `applyNextFetchPolicy` must be called.
916+
typeof options.nextFetchPolicy === "function")
914917
) {
915918
this.applyNextFetchPolicy("variables-changed", options);
916919
if (newNetworkStatus === void 0) {

Diff for: src/react/hooks/__tests__/useQuery.test.tsx

+127
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
OperationVariables,
1212
TypedDocumentNode,
1313
WatchQueryFetchPolicy,
14+
WatchQueryOptions,
1415
} from "../../../core";
1516
import { InMemoryCache } from "../../../cache";
1617
import { ApolloProvider } from "../../context";
@@ -36,6 +37,7 @@ import {
3637
} from "../../../testing/internal";
3738
import { useApolloClient } from "../useApolloClient";
3839
import { useLazyQuery } from "../useLazyQuery";
40+
import { mockFetchQuery } from "../../../core/__tests__/ObservableQuery";
3941

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

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

70727074
expect(reasons).toEqual(["variables-changed", "after-fetch"]);
70737075
});
7076+
7077+
it("should prioritize a `nextFetchPolicy` function over a `fetchPolicy` option when changing variables", async () => {
7078+
const query = gql`
7079+
{
7080+
hello
7081+
}
7082+
`;
7083+
const link = new MockLink([
7084+
{
7085+
request: { query, variables: { id: 1 } },
7086+
result: { data: { hello: "from link" } },
7087+
delay: 10,
7088+
},
7089+
{
7090+
request: { query, variables: { id: 2 } },
7091+
result: { data: { hello: "from link2" } },
7092+
delay: 10,
7093+
},
7094+
]);
7095+
7096+
const client = new ApolloClient({
7097+
cache: new InMemoryCache(),
7098+
link,
7099+
});
7100+
7101+
const mocks = mockFetchQuery(client["queryManager"]);
7102+
7103+
const expectQueryTriggered = (
7104+
nth: number,
7105+
fetchPolicy: WatchQueryFetchPolicy
7106+
) => {
7107+
expect(mocks.fetchQueryByPolicy).toHaveBeenCalledTimes(nth);
7108+
expect(mocks.fetchQueryByPolicy).toHaveBeenNthCalledWith(
7109+
nth,
7110+
expect.anything(),
7111+
expect.objectContaining({ fetchPolicy }),
7112+
expect.any(Number)
7113+
);
7114+
};
7115+
let nextFetchPolicy: WatchQueryOptions<
7116+
OperationVariables,
7117+
any
7118+
>["nextFetchPolicy"] = (_, context) => {
7119+
if (context.reason === "variables-changed") {
7120+
return "cache-and-network";
7121+
} else if (context.reason === "after-fetch") {
7122+
return "cache-only";
7123+
}
7124+
throw new Error("should never happen");
7125+
};
7126+
nextFetchPolicy = jest.fn(nextFetchPolicy);
7127+
7128+
const { result, rerender } = renderHook<
7129+
QueryResult,
7130+
{
7131+
variables: { id: number };
7132+
}
7133+
>(
7134+
({ variables }) =>
7135+
useQuery(query, {
7136+
fetchPolicy: "network-only",
7137+
variables,
7138+
notifyOnNetworkStatusChange: true,
7139+
nextFetchPolicy,
7140+
}),
7141+
{
7142+
initialProps: {
7143+
variables: { id: 1 },
7144+
},
7145+
wrapper: ({ children }) => (
7146+
<ApolloProvider client={client}>{children}</ApolloProvider>
7147+
),
7148+
}
7149+
);
7150+
// first network request triggers with initial fetchPolicy
7151+
expectQueryTriggered(1, "network-only");
7152+
7153+
await waitFor(() => {
7154+
expect(result.current.networkStatus).toBe(NetworkStatus.ready);
7155+
});
7156+
7157+
expect(nextFetchPolicy).toHaveBeenCalledTimes(1);
7158+
expect(nextFetchPolicy).toHaveBeenNthCalledWith(
7159+
1,
7160+
"network-only",
7161+
expect.objectContaining({
7162+
reason: "after-fetch",
7163+
})
7164+
);
7165+
// `nextFetchPolicy(..., {reason: "after-fetch"})` changed it to
7166+
// cache-only
7167+
expect(result.current.observable.options.fetchPolicy).toBe("cache-only");
7168+
7169+
rerender({
7170+
variables: { id: 2 },
7171+
});
7172+
7173+
expect(nextFetchPolicy).toHaveBeenNthCalledWith(
7174+
2,
7175+
// has been reset to the initial `fetchPolicy` of "network-only" because
7176+
// we changed variables, then `nextFetchPolicy` is called
7177+
"network-only",
7178+
expect.objectContaining({
7179+
reason: "variables-changed",
7180+
})
7181+
);
7182+
// the return value of `nextFetchPolicy(..., {reason: "variables-changed"})`
7183+
expectQueryTriggered(2, "cache-and-network");
7184+
7185+
await waitFor(() => {
7186+
expect(result.current.networkStatus).toBe(NetworkStatus.ready);
7187+
});
7188+
7189+
expect(nextFetchPolicy).toHaveBeenCalledTimes(3);
7190+
expect(nextFetchPolicy).toHaveBeenNthCalledWith(
7191+
3,
7192+
"cache-and-network",
7193+
expect.objectContaining({
7194+
reason: "after-fetch",
7195+
})
7196+
);
7197+
// `nextFetchPolicy(..., {reason: "after-fetch"})` changed it to
7198+
// cache-only
7199+
expect(result.current.observable.options.fetchPolicy).toBe("cache-only");
7200+
});
70747201
});
70757202

70767203
describe("Missing Fields", () => {

Diff for: src/tsconfig.json

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
{
66
"compilerOptions": {
77
"noEmit": true,
8+
"declaration": false,
9+
"declarationMap": false,
810
"lib": ["es2015", "esnext.asynciterable", "ES2021.WeakRef"],
911
"types": ["jest", "node", "./testing/matchers/index.d.ts"]
1012
},

0 commit comments

Comments
 (0)