Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Stack same queries with different variables #1594

Closed
fasibio opened this issue Jul 26, 2023 · 13 comments
Closed

Bug: Stack same queries with different variables #1594

fasibio opened this issue Jul 26, 2023 · 13 comments

Comments

@fasibio
Copy link

fasibio commented Jul 26, 2023

Tested Versions:

  • 3.0.0-alpha-7906fb34.0
  • 3.0.0-alpha-577a9298.0
  • 3.0.0-alpha-e9f8f97b.0

Simple Component:

import { useState } from 'react';

import { useQuery } from '../gqty';

export const Test = () => {
  const [tagSearchText, setTagSearchText] = useState<string>('search1');

  const { queryTag } = useQuery();

  const tagQueryResult1 = queryTag({
    filter: { name: { containsi: tagSearchText } },
  });

  return (
    <>
      <p>
        Search 1:{' '}
        {tagQueryResult1?.data.map((a) => ({
          id: a.id,
          name: a.name,
        })).length ?? 0}
      </p>
      <button
        onClick={() => {
          setTagSearchText('search2');
        }}
      >
        search html
      </button>
    </>
  );
};

Component description:

Make a initial Search for text search1 after button click. searchtext will change and second search (search2) will be execute.

Problem:

After Button click he Batch both Queries together.

After Pageload network content look like:

{
    "query": "query($a6e6b2:TagFiltersInput){a4be8:queryTag(filter:$a6e6b2){__typename data{__typename id name}}}",
    "variables": {
        "a6e6b2": {
            "name": {
                "containsi": "search1"
            }
        }
    }
}

After Buttonclick:

{
    "query": "query($a342b4:TagFiltersInput$a6e6b2:TagFiltersInput){a4be8:queryTag(filter:$a6e6b2){__typename data{__typename id name}}a8bf9:queryTag(filter:$a342b4){__typename data{__typename id name}}}",
    "variables": {
        "a6e6b2": {
            "name": {
                "containsi": "search1"
            }
        },
        "a342b4": {
            "name": {
                "containsi": "search2"
            }
        }
    }
}

you see search1 was execute again after buttonclick... this stack goes on and on search3, search4....

it looks like he stack the history... but why and how can I change it ?

@cdreier
Copy link

cdreier commented Jul 27, 2023

Hi,

i can confirm this behaviour.

for easier testing and reproducability, i added a minimal example for my testing api here

https://github.com/cdreier/gqty_rendering/blob/master/src/App_Search.tsx

the queries stack up on every keystroke, as described above

@vicary
Copy link
Member

vicary commented Jul 27, 2023

It is caused by object-hash including values, an upcoming patch will only use keys.

@vicary

This comment was marked as outdated.

@cdreier
Copy link

cdreier commented Jul 31, 2023

Hi, i tested this and found a minimum of the last two queries in the request, most of the time even more. It is much better than before, as it does no longer stacks indefinitly, but there is still too much fetching going on

@vicary
Copy link
Member

vicary commented Jul 31, 2023

@cdreier Not sure what kind of fetching we're talking about, is it possible to show a video or minimum repo?

@cdreier
Copy link

cdreier commented Jul 31, 2023

i used this repo and code for testing

https://github.com/cdreier/gqty_rendering/blob/master/src/App_Search.tsx

2023-07-31.11-57-12.mp4

when typing slowly, you can see, that the query contains always the current and the last query - so we fetch always the unneeded query from the last event

@vicary
Copy link
Member

vicary commented Jul 31, 2023

This could be caused by the concurrent mode in React 18, where it renders twice at once, and gqty batches them together in a single fetch.

You may wrap the state variable in useDeferredValue to further reduce the fetch.

The official docs has examples on how to do it.

@fasibio
Copy link
Author

fasibio commented Jul 31, 2023

I test it with component on top of issue:

Initial Query:

{
    "query": "query($fdd4d7:ID!){a13b6:getProject(id:$fdd4d7){__typename id title}}",
    "variables": {
        "fdd4d7": 1
    }
}

First Button Click:

{
    "query": "query($a7cf52:ID!$fdd4d7:ID!){a13b6:getProject(id:$fdd4d7){__typename id title}a8857:getProject(id:$a7cf52){__typename id title}}",
    "variables": {
        "fdd4d7": 1,
        "a7cf52": 2
    }
}

Second Button Click:

{
    "query": "query($a7cf52:ID!$d349f1:ID!){a2377:getProject(id:$d349f1){__typename id title}a8857:getProject(id:$a7cf52){__typename id title}}",
    "variables": {
        "a7cf52": 2,
        "d349f1": 3
    }
}

So I also have 2 queries with 2 variables.
And also with:

  const [tagSearchText, setTagSearchText] = useState<number>(1);

  const { getProject } = useQuery();

  const tagQueryResult1 = getProject({
    id: useDeferredValue(tagSearchText),
  });

I get the same result

@vicary
Copy link
Member

vicary commented Jul 31, 2023

Concurrent mode is kind of greedy, useDeferredValue tries to render back to back. When you enter the search text, multiple queries will be batched together, counting from the start of last query to the end of the immediate resulting render.

If you really need clean fetches, I recommend using the prepare option, or the useLazyQuery hook instead.

@cdreier
Copy link

cdreier commented Aug 1, 2023

Hi @vicary , thanks a lot for your help!

i tried to make another video the show the problem - i also added a few buttons to set the search term, so we can make sure, there is no greedy rendering issue and nothing is batched.

2023-08-01.08-51-44.mp4

as you can see, the query always sends the current query, and the previously sent query - i also played around with deferred values, but this doesn't change anything in this case.

@vicary

This comment was marked as outdated.

@vicary
Copy link
Member

vicary commented Aug 5, 2023

My apologies for the confusion @fasibio @cdreier.

Turns out I was testing with a patched local copy when looking at this issue, this issue happens to be fixed by that change.

I cherry picked relevant changes here, please see if 3.0.0-alpha-eb61be5a.0 fixes your issue.

@vicary
Copy link
Member

vicary commented Jan 30, 2024

This should be fixed in canary, please reopen if this happens again.

@vicary vicary closed this as completed Jan 30, 2024
vicary added a commit that referenced this issue Apr 21, 2024
* feat(package/core): scoped query
* feat(package/subscriptions): drop the package for graphql-ws
* feat(packages/utils): drop unused package
* fix(packages/logger): scoped query shim
* feat: refactor resolvers for react
* chore: error message
* feat(packages/react): new core shim
* fix: facebook/react#26230
* fix: generated subscription client
* feat(packages/gqty): compat with queryFetcher and subsscriptionsClient
* chore(compat): queryFetcher and subscriptionsClient
* fix(package/react): infinite render loop
* feat(package/gqty): separate Cache instantiation from client
* chore(package/gqty): compat and optimized normalization
* fix(package/react): react hooks
* chore(deps): upgrade multidict
* feat(package/gqty): rename fetchPolicy to cachePolicy
* feat(package/gqty): add reload in cachePolicy
* fix(package/react): consistent API
* feat(package/react): Added onComplete for useMutation
* feat(packages/cli): interactive mode
* feat(examples): added the gnt example
* fix(package/react): missing fetches in useQuery
* fix(package/react): incorrect suspense in usePrepared
* feat(package/cli): rename introspectionOptions to introspections
* fix(deps): upgrade faulty multidict
* fix(ci): use esm imports
* fix(packages/gqty): compat, cache option should be optional.
* fix(ci): dlx errors
* feat(package/gqty): batching with microtask
* chore(package/cli): refactor generated client
* chore: remove legacy website
* feat(package/gqty): passthru return types of selectors
* chore(deps): upgrade testing-library for React 18
* fix(examples/vite): compatibility with v3
* chore(package/gqty): warn about empty selections
* fix(packages/gqty): pure peer dep of graphql-ws
* feat(package/gqty): edge compatible microtask
* chore(examples/github): remove stale example
* chore(deps): upgrade and dedupe typescript
* feat(package/gqty): added extensions option
* fix(packages/react): defaults to soft  to match the docs
* fix(packages/react): better simulate useTransactionQuery
* chore(docs): self-contained images in README.md
* fix(packages/react): flaky waitFor
* chore(deps): make type-fest optional
* feat(package/cli): error handling in generated client
* chore(package/cli): code-splitting for watch mode
* chore(deps): remove ws dependency
* fix(package/react): infinite render loop
* fix(package/react): early cache hydration
* fix(package/react): default  mode
* feat(core): even more stickier fetches
* chore: verbatimModuleSyntax
* chore: test grommet fetch loop
* fix(package/react): properly dispose cache subscribers
* chore(deps): update esbuild
* feat(cli): prompt for destination
* fix(packages/react): infinite fetch via stale proxy #1588
* feat(examples/gnt): add bundle analyzer
* fix(cli): compat with config.introspection
* fix(cli): fetch all schemas before merging
* fix(cli): install peer dep 'graphql'
* fix(cli): allow disable subscription via arg
* fix(packages/cli): typo on options definition
* feat(cli): add negative options
* fix(gqty): safe auto-selection on nullable interfaces
* feat(gqty): remove default return from resolve()
* fix(package/react): clear selections post-fetch in useQuery (#1594)
* chore(compat): normalize import.meta.url to increase compatibility.
* fix(package/gqty, package/react): error handling
* feat(chore): prettier v3
* fix(cli): revert prettier to v2, deps are not ready for it
* fix(package/react): reduce over-fetching between renders #1594
* feat(cli): enable suspense in the generated client
* chore(examples/gnt): simulate noverby's infinite fetch bug
* fix(package/react): reduce infinite fetch when normalization is disabled
* fix(package/react): prevent non-query types from sticky fetching
* chore(ci): test theguild's snapshot action
* fix(cli): missing default import in watch mode
* feat(cli): do not exit on fetch errors during watch mode
* chore(deps): remove unused packages
* chore(lint): add prettier-plugin-jsdoc
* feat(package/react): add extensions option to useMutation
* chore(prettier): remove jsdoc plugin to prevent noisy changes
* feat(package/react): increase compatibility with non-web environments
* fix(package/react): prevent infinite fetches
* feat(package/react): experimental greedy fetch
* feat(package/gqty): expose aliasLength option
* feat(package/gqty): supress empty warnings when onEmptyResolve is specified
* chore(exampels/vite-react): upgrade deprecated deps
* feat(package/logger): fix fetch timer
* chore(packages/gqty): user communication on fallbacks
* chore(ci): fix release errors
* chore(packages/gqty): prevent fetching stale inputs
* chore(package/gqty): add mutation test for the new core
* chore(examples/vite-react): clean up for suspense
* fix(package/gqty): properly batch with microtask
* feat(package/cli): allow force disabling of react
* feat(package/react): add initialLoadingState option
* chore(package/gqty): tidy up file structure
* fix(packages/react): prevent fetch loops from cofetching resolvers
* fix(packages/gqty): disable initialLoadingState upon first fetch
* fix(package/gqty): rerender after first fetch to refresh isLoading
* chore(examples/gnt): upgrade nhost client
* chore: fix type resolutions in esm
* chore(packages/cli): TypeScript v5 friendly type imports
* chore(packages/react): reduce redundant iterations
* chore(deps): upgrade pnpm
* chore(examples/gnt): preparation work for Supabase example API
* chore(package/react): remove experimental feature
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants