query icon indicating copy to clipboard operation
query copied to clipboard

Properly type useInfiniteQuery to respect the select result

Open bradennapier opened this issue 3 years ago • 10 comments

Test: https://codesandbox.io/s/tannerlinsley-react-query-forked-gw8jm2?file=/src/index.tsx

Currently the type returned by useInfiniteQuery is not honoring the result of the select function. This is due to the fact that it is wrapped at one point in InfiniteData<TData> which strips the context once it is passed to the Query which doesn't have any wrapping.

The solution was to allow InfiniteData to have two parameters, one would be the normal TData and the other is a type parameter SData which represents the result of the select fn. This way it doesn't strip the context and can store both. The SData ends up taking precedence, but rules do still exist:

  • Select must return a compatible type to InfiniteData<TData> (it must have a pages and pagesParams property and both must be arrays, but they can be made a diff type if needed).
  • Any extra keys can be returned on top of those keys and the type is automatically inferred.

Such that:

type FetchResult = {
  totalPages: number;
  page: number;
  items: {
    n: number;
  }[];
};

async function exampleFetch(page: number): Promise<FetchResult> {
  return {
    totalPages: 50,
    page,
    items: [{ n: page + 1 }, { n: page + 2 }, { n: page + 3 }],
  };
}

/*

function transformResult(data: InfiniteData<FetchResult>) {
  return {
    ...data,
    items: data.pages.flatMap(x => x.items),
  };
}

export function useExampleInfiniteQuery() {
  const result = useInfiniteQuery({
    queryKey: ['example'],
    queryFn: ({ pageParam = 1 }) => exampleFetch(pageParam),
    getNextPageParam: (lastPage: FetchResult) =>
      lastPage.page === lastPage.totalPages ? undefined : lastPage.page + 1,
    getPreviousPageParam: () => undefined,
    select: useCallback(data => transformResult(data), []),
  });
  
  // correct
  const data: InfiniteData<FetchResult, {
    items: {
        n: number;
    }[];
  }> | undefined = result.data

  return result.data?.items;
}
image

It also allows overwriting the pages and pagesParams keys, but it will error if the return transformation does not include them and they must be arrays.

So:

// @ts-expect-error - type must have pages and pageParams arrays
function transformResult(data: InfiniteData<FetchResult>) {
  return {
    items: data.pages.flatMap(x => x.items),
  };
}


/*
InfiniteData<never, {
    pages: never[];
    pageParams: never[];
    items: {
        n: number;
    }[];
}>
*/
function transformResult(data: InfiniteData<FetchResult>) {
  return {
    pages: [],
    pageParams: [],
    items: data.pages.flatMap(x => x.items),
  };
}

/*
InfiniteData<number, {
    pages: number[];
    pageParams: number[];
    items: {
        n: number;
    }[];
}>
*/
function transformResult(data: InfiniteData<FetchResult>) {
  return {
    pages: [1, 2, 3],
    pageParams: [1, 2, 3],
    items: data.pages.flatMap(x => x.items),
  };
}

closes #3065

bradennapier avatar May 19 '22 06:05 bradennapier

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
react-query ✅ Ready (Inspect) Visit Preview May 19, 2022 at 6:04AM (UTC)

vercel[bot] avatar May 19 '22 06:05 vercel[bot]

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 4cca36c65ca315d549b9cadfc84ca7f988c7951b:

Sandbox Source
tannerlinsley/react-query: basic Configuration
tannerlinsley/react-query Configuration

codesandbox-ci[bot] avatar May 19 '22 06:05 codesandbox-ci[bot]

Codecov Report

Merging #3625 (9f6c992) into master (5848fab) will not change coverage. The diff coverage is n/a.

:exclamation: Current head 9f6c992 differs from pull request most recent head 4cca36c. Consider uploading reports for the commit 4cca36c to get more accurate results

@@           Coverage Diff           @@
##           master    #3625   +/-   ##
=======================================
  Coverage   96.35%   96.35%           
=======================================
  Files          45       45           
  Lines        2278     2278           
  Branches      637      637           
=======================================
  Hits         2195     2195           
  Misses         80       80           
  Partials        3        3           
Impacted Files Coverage Δ
src/core/mutation.ts 100.00% <ø> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 5848fab...4cca36c. Read the comment docs.

codecov[bot] avatar May 19 '22 11:05 codecov[bot]

Not sure why this failed for node 16? Seems odd it works for 14 and 12? It says that the objects are not equal - I did not make a single change to the codebase, it was all type level changes. Not to mention 12 and 14 were fine... and i developed with 16 and the tests passed.

bradennapier avatar May 21 '22 23:05 bradennapier

Not sure why this failed for node 16? Seems odd it works for 14 and 12? It says that the objects are not equal - I did not make a single change to the codebase, it was all type level changes. Not to mention 12 and 14 were fine... and i developed with 16 and the tests passed.

seems like a flaky test :( I've re-run the pipeline

TkDodo avatar May 23 '22 06:05 TkDodo

can you please add a type level test that shows that these things are working?

we have some examples, e.g. here:

https://github.com/tannerlinsley/react-query/blob/9f6c992feecb4694758f8767063d8ad89d842c3c/src/react/tests/useQueries.test.tsx#L473-L498

TkDodo avatar May 29 '22 18:05 TkDodo

Will do

bradennapier avatar May 30 '22 09:05 bradennapier

I am investigating a better fix which will actually work as it actually works but I'm not sure it'll be possible so we may have to include the extra rule for typescript (for now) unless we want to do a major (complete) overhaul to the type design.

Although I have realized there's a problem with infinite query select in that if you use it to transform data you have to transform all elements every time it runs. I understand the reason for it, but I do think there are fundamental concerns with this fn that is worth working out in another place.

bradennapier avatar Jun 01 '22 04:06 bradennapier

@bradennapier how are we with this PR? We have released v4 in the meantime, so there are a couple of conflicts that would need to be addressed please

TkDodo avatar Jul 30 '22 06:07 TkDodo

I am investigating a better fix which will actually work

@bradennapier what's the status here please?

TkDodo avatar Aug 16 '22 08:08 TkDodo

FYI, I will close stale PRs somewhen next week. Please either address the open comments / resolve the merge conflicts or this PR will be closed. Thanks 🙏

TkDodo avatar Sep 23 '22 14:09 TkDodo

if someone wants to pick up this PR where it left off, please feel free. But no feedback for 3 months means I'm gonna close this stale PR.

TkDodo avatar Oct 09 '22 08:10 TkDodo

Any update on this? Or type of select is still not inferred for infinite query?

joseito-terence avatar May 17 '23 05:05 joseito-terence

It's fixed in v5

TkDodo avatar May 17 '23 06:05 TkDodo