Skip to content

Commit

Permalink
Update to react-query@5 (connectrpc#179)
Browse files Browse the repository at this point in the history
Though most of the types have stayed the same, there are a few breaking
changes to be aware about:

- Removed `onError` passed to `useQuery`, this aligns with the removed
event handlers in
[@tanstack/query](https://tanstack.com/query/v5/docs/react/guides/migrating-to-v5#callbacks-on-usequery-and-queryobserver-have-been-removed)
- `useInfiniteQuery` now requires the initial page param to be defined
in the input because `initialPageParam` is
[required](https://tanstack.com/query/v5/docs/react/guides/migrating-to-v5#infinite-queries-now-need-a-initialpageparam)

This will require a bump to minor version.
  • Loading branch information
paul-sachs authored Oct 23, 2023
1 parent fd3253b commit 3252e24
Show file tree
Hide file tree
Showing 8 changed files with 637 additions and 583 deletions.
2 changes: 1 addition & 1 deletion examples/react/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@connectrpc/connect-web": "^1.1.2",
"@connectrpc/protoc-gen-connect-es": "^1.1.2",
"@connectrpc/protoc-gen-connect-query": "^0.5.1",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-query": "^5.0.0",
"@tanstack/react-query-devtools": "^4.36.1",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.0.0",
Expand Down
14 changes: 4 additions & 10 deletions examples/react/basic/src/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,15 @@ import { Page } from "./page";
* This example demonstrates a basic usage of Connect-Query with `useQuery`
*/
export const Example: FC = () => {
const {
status,
fetchStatus,
error,
//^? const error: ConnectError | null
data,
//^? const data: SayResponse | undefined
} = useQuery(say.useQuery({}));
// ^? const say: UnaryHooks<SayRequest, SayResponse>
const { status, fetchStatus, error, data } = useQuery({
...say.useQuery({}),
});

return (
<Page>
Status: {status}
<Indicators label="queryStatus">
<Indicator label="loading" parent={status} />
<Indicator label="pending" parent={status} />
<Indicator label="success" parent={status} />
<Indicator label="error" parent={status} />
</Indicators>
Expand Down
4 changes: 2 additions & 2 deletions packages/connect-query/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@connectrpc/connect": "^1.1.2",
"@connectrpc/connect-web": "^1.1.2",
"@connectrpc/protoc-gen-connect-es": "^1.1.2",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-query": "^5.0.0",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.2.30",
"@types/react-dom": "^18.2.14",
Expand All @@ -52,7 +52,7 @@
"peerDependencies": {
"@bufbuild/protobuf": "^1.3.3",
"@connectrpc/connect": "^1.1.2",
"@tanstack/react-query": "4.x",
"@tanstack/react-query": "^5.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
196 changes: 39 additions & 157 deletions packages/connect-query/src/create-unary-functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,17 @@ import type {
PartialMessage,
} from "@bufbuild/protobuf";
import { MethodKind } from "@bufbuild/protobuf";
import type { CallOptions, ConnectError, Transport } from "@connectrpc/connect";
import {
afterAll,
beforeEach,
describe,
expect,
it,
jest,
} from "@jest/globals";
import type { CallOptions, Transport } from "@connectrpc/connect";
import { Code, ConnectError } from "@connectrpc/connect";
import { describe, expect, it, jest } from "@jest/globals";
import type {
GetNextPageParamFunction,
QueryFunction,
QueryFunctionContext,
UseMutationResult,
} from "@tanstack/react-query";
import { useInfiniteQuery, useMutation, useQuery } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
import { spyOn } from "jest-mock";

import type {
ConnectPartialQueryKey,
Expand Down Expand Up @@ -62,8 +56,6 @@ import {
import type { DisableQuery } from "./utils";
import { disableQuery } from "./utils";

const consoleErrorSpy = spyOn(console, "error").mockImplementation(() => {});

const genCount = createUnaryFunctions({
methodInfo: BigIntService.methods.count,
typeName: BigIntService.typeName,
Expand Down Expand Up @@ -175,7 +167,7 @@ describe("createUnaryFunctions", () => {

describe("setQueriesData", () => {
it("returns the correct queryKey", () => {
const [queryKey] = genCount.setQueriesData(partialUpdater);
const [{ queryKey }] = genCount.setQueriesData(partialUpdater);
type ExpectType_QueryKey = Expect<
Equal<typeof queryKey, ConnectPartialQueryKey>
>;
Expand Down Expand Up @@ -302,7 +294,12 @@ describe("createUnaryFunctions", () => {
>;

type ExpectType_UseInfiniteQueryParams0 = Expect<
Equal<params[0], DisableQuery | PartialMessage<CountRequest>>
Equal<
params[0],
| DisableQuery
| (PartialMessage<CountRequest> &
Required<Pick<PartialMessage<CountRequest>, "add">>)
>
>;

type returnType = ReturnType<
Expand All @@ -312,7 +309,12 @@ describe("createUnaryFunctions", () => {
type ExpectType_UseInfiniteQueryReturnKeys = Expect<
Equal<
keyof returnType,
"enabled" | "getNextPageParam" | "onError" | "queryFn" | "queryKey"
| "enabled"
| "getNextPageParam"
| "initialPageParam"
| "queryFn"
| "queryKey"
| "throwOnError"
>
>;

Expand All @@ -322,14 +324,17 @@ describe("createUnaryFunctions", () => {
{
enabled: boolean;
queryKey: ConnectQueryKey<CountRequest>;
queryFn: (
context: QueryFunctionContext<
ConnectQueryKey<CountRequest>,
bigint | undefined
>,
) => Promise<CountResponse>;
getNextPageParam: GetNextPageParamFunction<CountResponse>;
onError?: (error: ConnectError) => void;
queryFn: QueryFunction<
CountResponse,
ConnectQueryKey<CountRequest>,
bigint | undefined
>;
getNextPageParam: GetNextPageParamFunction<
bigint | undefined,
CountResponse
>;
throwOnError?: (error: ConnectError) => boolean;
initialPageParam: bigint | undefined;
}
>
>;
Expand Down Expand Up @@ -377,7 +382,9 @@ describe("createUnaryFunctions", () => {
it("requires a transport", () => {
expect(() => {
genCount.createUseInfiniteQueryOptions(
{},
{
add: 0n,
},
{
getNextPageParam: (lastPage) => lastPage.count + 1n,
pageParamKey: "add",
Expand All @@ -404,14 +411,10 @@ describe("createUnaryFunctions", () => {
wrapper({ defaultOptions }),
);

expect(result.current.data).toStrictEqual(undefined);

await waitFor(() => {
expect(result.current.isSuccess).toBeTruthy();
});

expect(result.current.data?.pageParams).toStrictEqual([undefined]);

expect(result.current.data?.pages).toHaveLength(1);
expect(result.current.data?.pages[0].page).toStrictEqual(1n);
expect(result.current.data?.pages[0].items).toStrictEqual([
Expand All @@ -424,8 +427,6 @@ describe("createUnaryFunctions", () => {
await result.current.fetchNextPage();
rerender();

expect(result.current.data?.pageParams).toStrictEqual([undefined, 2n]);

expect(result.current.data?.pages).toHaveLength(2);
expect(result.current.data?.pages[1].page).toStrictEqual(2n);
expect(result.current.data?.pages[1].items).toStrictEqual([
Expand Down Expand Up @@ -489,70 +490,13 @@ describe("createUnaryFunctions", () => {
pageParam: 1n,
queryKey: genCount.getQueryKey(input),
meta: {},
direction: "forward",
signal: new AbortController().signal,
});

expect(count).toStrictEqual(1n);
});

describe("onError", () => {
beforeEach(() => {
consoleErrorSpy.mockReset();
});

it("doesn't use onError if it isn't passed", () => {
const { result } = renderHook(
() =>
genCount.createUseInfiniteQueryOptions(
{},
{
pageParamKey: "add",
getNextPageParam: (lastPage) => lastPage.count,
transport: mockEliza(),
},
),
wrapper(),
);
expect(result.current.onError).toBeUndefined();
expect(consoleErrorSpy).not.toHaveBeenCalled();
});

it("allows for a passthrough onError", async () => {
const onError = jest.fn();
const { result, rerender } = renderHook(
() =>
useQuery({
...genCount.createUseInfiniteQueryOptions(
// @ts-expect-error(2345) intentionally invalid input
{ nope: "nope nope" },
{ onError, pageParamKey: "add", transport: mockEliza() },
),
queryFn: async () => Promise.reject("error"),
retry: false,
}),
wrapper(undefined, mockBigInt()),
);
rerender();

expect(result.current.error).toStrictEqual(null);
expect(result.current.isError).toStrictEqual(false);
expect(onError).toHaveBeenCalledTimes(0);
expect(consoleErrorSpy).not.toHaveBeenCalled();

await waitFor(
() => {
expect(result.current.error).toStrictEqual("error");
},
{
timeout: 300,
},
);

expect(result.current.isError).toStrictEqual(true);
expect(onError).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("error");
});
});

it("passes through callOptions", () => {
const transport = mockBigInt();
const transportSpy = jest.spyOn(transport, "unary");
Expand Down Expand Up @@ -844,8 +788,9 @@ describe("createUnaryFunctions", () => {
onError,
transport: mockBigInt(),
}),
mutationFn: async () => Promise.reject("error"),
mutationKey: genCount.getQueryKey(),
mutationFn: () => {
throw new ConnectError("error", Code.Aborted);
},
}),
wrapper({ defaultOptions }),
);
Expand All @@ -855,7 +800,6 @@ describe("createUnaryFunctions", () => {
expect(result.current.error).toStrictEqual(null);
expect(result.current.isError).toStrictEqual(false);
expect(onError).toHaveBeenCalledTimes(0);
expect(consoleErrorSpy).toHaveBeenCalled();

type ExpectType_Error = Expect<
Equal<typeof result.current.error, ConnectError | null>
Expand All @@ -865,10 +809,9 @@ describe("createUnaryFunctions", () => {

await sleep(10);

expect(result.current.error).toStrictEqual("error");
expect(result.current.error?.code).toStrictEqual(Code.Aborted);
expect(result.current.isError).toStrictEqual(true);
expect(onError).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("error");
});

it("makes a mutation", async () => {
Expand Down Expand Up @@ -986,7 +929,6 @@ describe("createUnaryFunctions", () => {
getPlaceholderData?: (
enabled: boolean,
) => PartialMessage<SayResponse> | undefined;
onError?: (error: ConnectError) => void;
transport?: Transport | undefined;
callOptions?: CallOptions | undefined;
}
Expand All @@ -1008,26 +950,19 @@ describe("createUnaryFunctions", () => {
context?: QueryFunctionContext<ConnectQueryKey<SayRequest>>,
) => Promise<SayResponse>;
placeholderData?: () => SayResponse | undefined;
onError?: (error: ConnectError) => void;
throwOnError?: (error: ConnectError) => boolean;
}
>
>;

const result = genSay.createUseQueryOptions(undefined, {
onError: jest.fn(),
getPlaceholderData: jest.fn(() => new SayResponse()),
transport: mockEliza(),
});

expect(
Object.keys(result).sort((a, b) => a.localeCompare(b)),
).toStrictEqual([
"enabled",
"onError",
"placeholderData",
"queryFn",
"queryKey",
]);
).toStrictEqual(["enabled", "placeholderData", "queryFn", "queryKey"]);
});

describe("enabled", () => {
Expand Down Expand Up @@ -1160,59 +1095,6 @@ describe("createUnaryFunctions", () => {
});
});

describe("onError", () => {
beforeEach(() => {
consoleErrorSpy.mockReset();
});
afterAll(() => {
consoleErrorSpy.mockReset();
});

it("doesn't use onError if it isn't passed", () => {
const result = genSay.createUseQueryOptions(undefined, {
transport: mockEliza(),
});
expect(result.onError).toBeUndefined();
});

it("allows for a passthrough onError", async () => {
const onError = jest.fn();
const { result, rerender } = renderHook(
() =>
useQuery({
...genSay.createUseQueryOptions(
// @ts-expect-error(2345) intentionally invalid input
{ nope: "nope nope" },
{ onError, transport: mockEliza() },
),
queryFn: async () => Promise.reject("error"),
retry: false,
}),
wrapper(),
);
rerender();

expect(result.current.error).toStrictEqual(null);
expect(result.current.isError).toStrictEqual(false);
expect(onError).toHaveBeenCalledTimes(0);
expect(consoleErrorSpy).not.toHaveBeenCalled();

await waitFor(
() => {
expect(result.current.error).toStrictEqual("error");
},
{
timeout: 300,
},
);

expect(result.current.error).toStrictEqual("error");
expect(result.current.isError).toStrictEqual(true);
expect(onError).toHaveBeenCalledTimes(1);
expect(consoleErrorSpy).toHaveBeenCalledWith("error");
});
});

describe("queryFn", () => {
const input: PartialMessage<SayRequest> = { sentence: "ziltoid" };

Expand Down
Loading

0 comments on commit 3252e24

Please sign in to comment.