Skip to content

Commit

Permalink
chore: merge main -> next (#3508)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusmarminge committed Dec 27, 2022
2 parents 4b7a1b6 + 6475fa9 commit d274cd4
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 44 deletions.
52 changes: 39 additions & 13 deletions packages/react-query/src/createTRPCReact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
} from './shared/hooks/createHooksInternal';
import {
CreateClient,
DefinedUseTRPCQueryOptions,
DefinedUseTRPCQueryResult,
TRPCProvider,
UseDehydratedState,
UseTRPCInfiniteQueryOptions,
Expand All @@ -43,6 +45,42 @@ import {
} from './shared/hooks/types';
import { CreateTRPCReactOptions } from './shared/types';

/**
* @internal
*/
export interface ProcedureUseQuery<
TProcedure extends AnyProcedure,
TPath extends string,
> {
<
TQueryFnData = inferTransformedProcedureOutput<TProcedure>,
TData = inferTransformedProcedureOutput<TProcedure>,
>(
input: inferProcedureInput<TProcedure>,
opts: DefinedUseTRPCQueryOptions<
TPath,
inferProcedureInput<TProcedure>,
TQueryFnData,
TData,
TRPCClientErrorLike<TProcedure>
>,
): DefinedUseTRPCQueryResult<TData, TRPCClientErrorLike<TProcedure>>;

<
TQueryFnData = inferTransformedProcedureOutput<TProcedure>,
TData = inferTransformedProcedureOutput<TProcedure>,
>(
input: inferProcedureInput<TProcedure>,
opts?: UseTRPCQueryOptions<
TPath,
inferProcedureInput<TProcedure>,
TQueryFnData,
TData,
TRPCClientErrorLike<TProcedure>
>,
): UseTRPCQueryResult<TData, TRPCClientErrorLike<TProcedure>>;
}

/**
* @internal
*/
Expand All @@ -61,19 +99,7 @@ export type DecorateProcedure<
input: inferProcedureInput<TProcedure>,
type?: QueryType,
) => QueryKey;
useQuery: <
TQueryFnData = inferTransformedProcedureOutput<TProcedure>,
TData = inferTransformedProcedureOutput<TProcedure>,
>(
input: inferProcedureInput<TProcedure>,
opts?: UseTRPCQueryOptions<
TPath,
inferProcedureInput<TProcedure>,
TQueryFnData,
TData,
TRPCClientErrorLike<TProcedure>
>,
) => UseTRPCQueryResult<TData, TRPCClientErrorLike<TProcedure>>;
useQuery: ProcedureUseQuery<TProcedure, TPath>;
} & (inferProcedureInput<TProcedure> extends { cursor?: any }
? {
useInfiniteQuery: <
Expand Down
28 changes: 25 additions & 3 deletions packages/react-query/src/shared/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
DefinedUseQueryResult,
DehydratedState,
InfiniteQueryObserverSuccessResult,
InitialDataFunction,
QueryObserverSuccessResult,
QueryOptions,
UseInfiniteQueryOptions,
Expand Down Expand Up @@ -49,6 +51,17 @@ export interface UseTRPCQueryOptions<TPath, TInput, TOutput, TData, TError>
extends UseQueryOptions<TOutput, TError, TData, [TPath, TInput]>,
TRPCUseQueryBaseOptions {}

/** @internal **/
export interface DefinedUseTRPCQueryOptions<
TPath,
TInput,
TOutput,
TData,
TError,
> extends UseTRPCQueryOptions<TPath, TInput, TOutput, TData, TError> {
initialData: TOutput | InitialDataFunction<TOutput>;
}

export interface TRPCQueryOptions<TPath, TInput, TData, TError>
extends QueryOptions<TData, TError, TData, [TPath, TInput]>,
TRPCUseQueryBaseOptions {}
Expand Down Expand Up @@ -98,15 +111,24 @@ export type CreateClient<TRouter extends AnyRouter> = (
/**
* @internal
*/

export type UseTRPCQueryResult<TData, TError> = UseQueryResult<TData, TError> &
TRPCHookResult;

/**
* @internal
*/
export type DefinedUseTRPCQueryResult<TData, TError> = DefinedUseQueryResult<
TData,
TError
> &
TRPCHookResult;

/**
* @internal
*/
export type UseTRPCQuerySuccessResult<TData, TError> =
QueryObserverSuccessResult<TData, TError> & TRPCHookResult;

/**
* @internal
*/
Expand All @@ -115,15 +137,15 @@ export type UseTRPCInfiniteQueryResult<TData, TError> = UseInfiniteQueryResult<
TError
> &
TRPCHookResult;

/**
* @internal
*/

export type UseTRPCInfiniteQuerySuccessResult<TData, TError> =
InfiniteQueryObserverSuccessResult<TData, TError> & TRPCHookResult;

/**
* @internal
*/

export type UseTRPCMutationResult<TData, TError, TVariables, TContext> =
UseMutationResult<TData, TError, TVariables, TContext> & TRPCHookResult;
84 changes: 56 additions & 28 deletions packages/tests/server/react/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,42 +76,70 @@ const ctx = konn()
})
.done();

test('useQuery()', async () => {
const { proxy, App } = ctx;
function MyComponent() {
const query1 = proxy.post.byId.useQuery({
id: '1',
});
describe('useQuery()', () => {
test('loading data', async () => {
const { proxy, App } = ctx;
function MyComponent() {
const query1 = proxy.post.byId.useQuery({
id: '1',
});

expect(query1.trpc.path).toBe('post.byId');
expect(query1.trpc.path).toBe('post.byId');

// @ts-expect-error Should not exist
proxy.post.byId.useInfiniteQuery;
const utils = proxy.useContext();

useEffect(() => {
utils.post.byId.invalidate();
// @ts-expect-error Should not exist
utils.doesNotExist.invalidate();
}, [utils]);
proxy.post.byId.useInfiniteQuery;
const utils = proxy.useContext();

if (!query1.data) {
return <>...</>;
useEffect(() => {
utils.post.byId.invalidate();
// @ts-expect-error Should not exist
utils.doesNotExist.invalidate();
}, [utils]);

if (!query1.data) {
return <>...</>;
}

type TData = typeof query1['data'];
expectTypeOf<TData>().toMatchTypeOf<'__result'>();

return <pre>{JSON.stringify(query1.data ?? 'n/a', null, 4)}</pre>;
}

type TData = typeof query1['data'];
expectTypeOf<TData>().toMatchTypeOf<'__result'>();
const utils = render(
<App>
<MyComponent />
</App>,
);
await waitFor(() => {
expect(utils.container).toHaveTextContent(`__result`);
});
});

return <pre>{JSON.stringify(query1.data ?? 'n/a', null, 4)}</pre>;
}
test('data type without initialData', () => {
const expectation = expectTypeOf(() =>
ctx.proxy.post.byId.useQuery({ id: '1' }),
).returns;

const utils = render(
<App>
<MyComponent />
</App>,
);
await waitFor(() => {
expect(utils.container).toHaveTextContent(`__result`);
expectation.toMatchTypeOf<{ data: '__result' | undefined }>();
expectation.not.toMatchTypeOf<{ data: '__result' }>();
});

test('data type with initialData', () => {
const expectation = expectTypeOf(() =>
ctx.proxy.post.byId.useQuery(
{ id: '1' },
{
initialData: {
id: 1,
text: '',
},
},
),
).returns;

expectation.toMatchTypeOf<{ data: '__result' }>();
expectation.not.toMatchTypeOf<{ data: undefined }>();
});
});

Expand Down
125 changes: 125 additions & 0 deletions packages/tests/server/regression/issue-2996-defined-data.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { getServerAndReactClient } from '../react/__reactHelpers';
import { useQuery } from '@tanstack/react-query';
import { render, waitFor } from '@testing-library/react';
import { initTRPC } from '@trpc/server';
import { expectTypeOf } from 'expect-type';
import { konn } from 'konn';
import React from 'react';

const posts = [
{ id: 1, title: 'foo' },
{ id: 2, title: 'bar' },
];
type Post = typeof posts[number];

const fetchPosts = async () => posts;

const ctx = konn()
.beforeEach(() => {
const t = initTRPC.create();

const appRouter = t.router({
posts: t.procedure.query(fetchPosts),
});

return getServerAndReactClient(appRouter);
})
.afterEach(async (ctx) => {
await ctx?.close?.();
})
.done();

test('destructuring data', async () => {
const { App, proxy } = ctx;

function MyComponent() {
const { data: trpcData = [] } = proxy.posts.useQuery();
expectTypeOf<typeof trpcData>().toEqualTypeOf<Post[]>();

// verify tanstack returns the same
const { data: rqData = [] } = useQuery(['key'], fetchPosts);
expectTypeOf<typeof rqData>().toEqualTypeOf<Post[]>();

if (!trpcData) throw new Error('should not happen');

if (trpcData.length === 0) return <div>No posts</div>;
return <div>{trpcData.map((post) => post.title).join(', ')}</div>;
}

const utils = render(
<App>
<MyComponent />
</App>,
);

expect(utils.container).toHaveTextContent('No posts');
await waitFor(() => {
expect(utils.container).toHaveTextContent('foo, bar');
});
});

test('using `initialData`', async () => {
const { App, proxy } = ctx;

function MyComponent() {
const { data: trpcData } = proxy.posts.useQuery(undefined, {
initialData: [],
});
expectTypeOf<typeof trpcData>().toEqualTypeOf<Post[]>();

// verify tanstack returns the same
const { data: rqData } = useQuery(['key'], fetchPosts, {
initialData: [],
});
expectTypeOf<typeof rqData>().toEqualTypeOf<Post[]>();

if (!trpcData) throw new Error('should not happen');

if (trpcData.length === 0) return <div>No posts</div>;
return <div>{trpcData.map((post) => post.title).join(', ')}</div>;
}

const utils = render(
<App>
<MyComponent />
</App>,
);

expect(utils.container).toHaveTextContent('No posts');
await waitFor(() => {
expect(utils.container).toHaveTextContent('foo, bar');
});
});

test('using `placeholderData`', async () => {
const { App, proxy } = ctx;

function MyComponent() {
const { data: trpcData } = proxy.posts.useQuery(undefined, {
placeholderData: [],
});
expectTypeOf<typeof trpcData>().toEqualTypeOf<Post[] | undefined>();

// verify tanstack returns the same
const { data: rqData } = useQuery(['key'], fetchPosts, {
placeholderData: [],
});
expectTypeOf<typeof rqData>().toEqualTypeOf<Post[] | undefined>();

if (!trpcData) throw new Error('should not happen');

if (trpcData.length === 0) return <div>No posts</div>;
return <div>{trpcData.map((post) => post.title).join(', ')}</div>;
}

const utils = render(
<App>
<MyComponent />
</App>,
);

expect(utils.container).toHaveTextContent('No posts');
await waitFor(() => {
expect(utils.container).toHaveTextContent('foo, bar');
});
});

0 comments on commit d274cd4

Please sign in to comment.