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

Unable to use react-query when hooks are created in separate library #3595

Closed
SoftMemes opened this issue May 9, 2022 · 51 comments
Closed

Comments

@SoftMemes
Copy link

Describe the bug

I have a monorepo with yarn 3 workspaces consisting of the following (as well as other components):

  • contracts which uses Orval to create typed react-query hooks from OpenAPI
  • ui, a next.js project that has a workspace dependency on contracts and is using above hooks

The only direct dependencies on react-query are through contracts, my UI project does not directly use react-query.

I'm now running into a few issues to do with module resolution, and am not sure how much of this relates to yarn, workspaces, react-query, or next magic.

React-query cannot find QueryProvider

If I make react-query a peer dependency of contracts, then initialize react-query in my next project (set a query provider, etc), then the hooks that use react-query under the hood from contracts do not find the QueryClient. It appears to "see" its own version of react-query even though this is a peer dependency of contracts and I provide it as a direct dependency in ui.

I am able to work around this by having contracts instead directly depend on react-query, and re-export the symbols from react-query (export * from 'react-query'). This appears to give the hooks in contracts` access to the same react-query and the context / query provider is found.

... or at least it was. Without any meaningful changes (I'm working on pinning down the exact change), I am no longer able to do it this way, as react-query now fails to find react. Inside the compiled code of react-query, I am getting React as an undefined symbol and this line blows up var defaultContext = /*#__PURE__*/React.createContext(undefined); iin react/QueryClientProvider.js

I appreciate that this isn't much to go on, but in general, what is the recommended method for providing custom hooks based on react-query in a library in terms of dependencies and configuration of the query client?

Your minimal, reproducible example

N/A

Steps to reproduce

  1. Build a library that leverages react-query in custom hooks with react-query as a peer dependency
  2. Use the library from above in a different project, providing react-query as a direct dependency
  3. Configure react-query with a QueryClientProvider in the dom where the hooks from 1 are being used

Expected behavior

React-query can "see" the context and query provider wired up in the react DOM, even when using peer dependencies and the react-query library is provided as a dependency form the parent project.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

  • OS: Ubuntu 20
  • Yarn: 3.2.0
  • Next.js: 12.1.6

react-query version

3.39.0

TypeScript version

4.6.4

Additional context

No response

@mauris
Copy link

mauris commented May 9, 2022

it's hard to tell what went wrong without knowing how your monorepo setup is like. I use nx for my monorepo setup, with the following packages:

  • one React app (entrypoint and build)
  • 3 React libraries, some of which provide custom hooks that compose useQuery, useQueries and useInfiniteQuery

I don't face the issue you've described on what I have:

  • OSes: Windows (developer), macOS (developer), Debian (docker/k8s containers)
  • react-query version: 3.39.0
  • TypeScript version: 4.6.4

might be worthwhile for you to share the problem in a minimal reproducible setup

@TkDodo
Copy link
Collaborator

TkDodo commented May 15, 2022

I appreciate that this isn't much to go on, but in general, what is the recommended method for providing custom hooks based on react-query in a library in terms of dependencies and configuration of the query client?

if you want a shared scope, meaning that the library uses the same context / client as the application that includes it, I would do it like you've described (I think):

  • have the lib define react-query as peerDependency
  • have the app create the queryClient and the QueryClientProvider.

the useQuery calls in the lib should just pick up that client. If that's not the case, you likely have either two versions of react-query or two versions of react around.

if you want an isolated scope, I would take a look at the custom context feature for v4.

@TkDodo
Copy link
Collaborator

TkDodo commented Jun 4, 2022

Any updates here? Is this still an issue?

@nctay
Copy link

nctay commented Jun 26, 2022

Having same issue with turborepo monorepo setup, extracting queries to different package causes it.

@TkDodo
Copy link
Collaborator

TkDodo commented Jun 27, 2022

@nctay can you link to an example repo that has the issue please?

@petercpwong
Copy link

Having the same issue as well with a NextJS pnpm monorepo setup, even after upgrading to react-query v4. Had to pass in defaultContext to every single hook to get it to work. I'm also using next-transpile-modules so that could be what's causing the issue.

It works flawlessly with a vite pnpm monorepo setup though.

@stamahto
Copy link

I have same issue too

@TkDodo
Copy link
Collaborator

TkDodo commented Jul 24, 2022

It's honestly not very helpful stating that you have the issue too without providing a minimal reproduction that we can look into. So far, no one has created a reproduction, so there is nothing I can look into.
I'm gonna close this now, feel free to reopen once you can share a reproduction.

@TkDodo TkDodo closed this as not planned Won't fix, can't repro, duplicate, stale Jul 24, 2022
@mverissimo
Copy link

I faced this issue too, you need to check if have the equal version of react and react-query and both packages.

@stamahto
Copy link

I faced this issue too, you need to check if have the equal version of react and react-query and both packages.

I do, still not working. I´m gonna have to learn publishing packages into npmjs and create reproduction case. It will take me some time, but I hope it will be worthy.

@SoftMemes
Copy link
Author

Hi,

Not a repro, and I'm travelling so will be a brief message from the phone, but I know at least what caused this now. Using yarn 3 with a pnpm style repo (this may apply to other setups but this is where I've seen it), a dependency will only be shared if it matches exactly. This means that the same version has to be resolved which is easy enough to guarantee with version ranges but also critically, the same dependencies need to have been resolved.

This interacts with in particular dev dependencies. If the project A dependends on library B, both which use a third party package C (for example react query), but A and B have direct dependencies in such a way that the dev dependencies of C are fulfilled by different versions, then A and B will see different instances of C.

The way to check this is to compare where the symlink for C is pointing in node modules for A vs B, the compare the node modules in turn for the instance of C used by A vs C used by B, and tweak dependencies of each until they match perfectly.

@marissync
Copy link

Having the same issue as well with a NextJS pnpm monorepo setup, even after upgrading to react-query v4. Had to pass in defaultContext to every single hook to get it to work. I'm also using next-transpile-modules so that could be what's causing the issue.

It works flawlessly with a vite pnpm monorepo setup though.

How did you do that? Facing same issue in next.js

@petercpwong
Copy link

petercpwong commented Jul 28, 2022

@marissync The useQuery hook accepts a context option. Just pass in the defaultContext exported by react query.

import { defaultContext, useQuery } from '@tanstack/react-query';

useQuery({ context: defaultContext });

@marissync
Copy link

@marissync The useQuery hook accepts a context option. Just pass in the defaultContext exported by react query.

import { defaultContext, useQuery } from '@tanstack/react-query';

useQuery({ context: defaultContext });

Thank you, @petercpwong!!

@wiredmatt
Copy link

Instead of asking for a failing reproduction wouldn't it be easier to provide a working one? I've tried what was suggested above but it's still failing in the production bundle. I'm using exact versions of react and react-query.

fgnass added a commit to fgnass/react-api-query that referenced this issue Aug 10, 2022
@arlyon
Copy link

arlyon commented Sep 14, 2022

I can corroborate the issue with next-transpile-modules.

Edit: @nctay @marissync @petercpwong I was able to resolve this issue by adding a resolve alias in my next.config.js webpack settings:

  // react-query causes issues with next-transpile-modules
  // so we need to override the resolutions to prevent duplicates
  webpack: (config, options) => {
    if (options.isServer) {
      config.externals = ["react-query", ...config.externals];
    }

    // this resolves to react-query/lib/index.js, so jump up two dirs
    const reactQuery = path.resolve(require.resolve("react-query"), "../../")

    config.resolve.alias['react-query'] = reactQuery
    config.resolve.alias['react-query/devtools'] = path.resolve(reactQuery, "devtools")

    return config;
  }

@luan-hte
Copy link

I had the same issue when using react-query with @tanstack/react-query-devtools. I removed react-query and installed @tanstack/react-query and the issue was gone. Basically use both packages from @TanStack.

@szmarci
Copy link

szmarci commented Sep 15, 2022

@system32uwu @SoftMemes @TkDodo I had the same issue. In this case for some reason pnpm installed two versions of react query (taken from the lockfile):

/@tanstack/react-query/4.3.4:
/@tanstack/react-query/4.3.4_sfoxds7t5ydpegc3knd667wn6m:

The problem went away when I changed peerDependencies in all of my libraries that included react-query as a peer dependecies to include react as well, so changing from this:

"peerDependencies": {
   "@tanstack/react-query": "^4.3.4"  
}

To this:

"peerDependencies": {
   "@tanstack/react-query": "^4.3.4",
   "react": "^17.0.2",
   "react-dom": "^17.0.2"
}

And after a pnpm install, only one version is installed:

/@tanstack/react-query/4.3.4_sfoxds7t5ydpegc3knd667wn6m:

@szmarci
Copy link

szmarci commented Sep 16, 2022

If someone wants to investigate it further, I created a minimal repository: https://github.com/szmarci/pnpm
Master branch is the working one, fail branch is... well, the failing one.

This is the only difference in the source files (fail branch -> master branch):
image

And after install these are the lockfile differences:
image

image

@jrea
Copy link

jrea commented Oct 12, 2022

in a monorepo, using a lib within the repo. I'm using yarn workspaces.

I added the lib as a peer dep, and added a webpack resolver in next.config.js like this:

  if (options.isServer) {
      config.externals = ['@tanstack/react-query', ...config.externals];
    }

    const reactQuery = path.resolve(require.resolve('@tanstack/react-query'));

    config.resolve.alias['@tanstack/react-query'] = reactQuery;

@ecyrbe
Copy link
Contributor

ecyrbe commented Oct 19, 2022

@TkDodo do you think the issue i linked above can come from esm support ? only one instance of @tanstack/react-query is installed, zodios is declaring dependencies as peer dependencies for both react-query and react.
i checked installed packages, no duplicate.
seems related to webpack resolution, webpack seems to create two instance duplicate instances of react-query in final build.
The bug was not happening on react-query v3, that's why i suspect esm modules.

@jrea workaround works like a charm, but it's ugly to tell zodios users to resolve to such hacks. i know ts-rest lib also has the same issue : ts-rest/ts-rest#66

@ecyrbe
Copy link
Contributor

ecyrbe commented Oct 19, 2022

@TkDodo do you want me to open a new issue for this ?

@ecyrbe
Copy link
Contributor

ecyrbe commented Oct 19, 2022

@TkDodo i confirm the bug appeared in @tanstack/react-query v4.3.0 and everything was fine on v4.3.2
I'll create an issue since i now have reproductible test case

@rametta
Copy link
Contributor

rametta commented Nov 11, 2022

@ecyrbe That doesn't seem to help unfortunately. I got something semi working by specify "type": "module" in my package.json and importing directly from the esm dir in my code, but I wish it was more automatic

@ecyrbe
Copy link
Contributor

ecyrbe commented Nov 11, 2022

There is an ugly workaround for next if all other things don't work (idea from @jrea ) : https://github.com/ecyrbe/zodios-express/blob/a237332b38cf0d8edb49edafb66c2d659a1d35f5/examples/next/next.config.js

@rametta
Copy link
Contributor

rametta commented Nov 11, 2022

Actually your previous comment did work, I just realized I needed to delete the Next cache (.next dir) before rebuilding because I was still getting the old package.json 🤦
What a rollercoaster 😄 Thanks again

@Darkhorse-Fraternity
Copy link

Darkhorse-Fraternity commented Nov 24, 2022

@rametta It is just useful in localhost. It fails when running in git action or docker. See here: https://github.com/Darkhorse-Fraternity/monad-stack/actions/runs/3538172759/jobs/5938774870#step:7:175

@rametta
Copy link
Contributor

rametta commented Nov 24, 2022

The solution that worked for me also worked in GitHub actions with a nextjs prod build consuming the package

@adairrr
Copy link

adairrr commented Dec 3, 2022

I was able to reproduce the issue, which seems to be something with yarn v3 dependency resolution here:
https://github.com/adairrr/missing-query-provider-repro

The interesting part of the issue is that running yarn why @tanstack/react-query elicits only one version:

yarn why @tanstack/react-query
└─ dapp@workspace:packages/dappery            
   └─ @tanstack/react-query@npm:4.19.0 [ca7b3] (via npm:^4.19.0 [ca7b3])

@emurano
Copy link

emurano commented Dec 9, 2022

vitejs/vite#6780 (comment)

Make sure your library has all peer dependencies listed in your rollup config in your vite config file: build.rollupOptions.external

@johnayoung
Copy link

Just want to add here that I also had this problem with my tsconfig.json. Removing additional "lib" declarations solved it for me:

Previous
{ "compilerOptions": { "target": "ES5", "lib": ["ES2017", "ES7", "ES6", "dom"] }, }

New
{ "compilerOptions": { "target": "ES5" }, }

@nctay
Copy link

nctay commented Dec 15, 2022

in a monorepo, using a lib within the repo. I'm using yarn workspaces.

I added the lib as a peer dep, and added a webpack resolver in next.config.js like this:

  if (options.isServer) {
      config.externals = ['@tanstack/react-query', ...config.externals];
    }

    const reactQuery = path.resolve(require.resolve('@tanstack/react-query'));

    config.resolve.alias['@tanstack/react-query'] = reactQuery;

Alias definitely works for this case.

React-query V4 update:

webpack: (config, options) => {
      if (options.isServer) {
        config.externals = ['@tanstack/react-query', ...config.externals]
      }
      config.resolve.alias['@tanstack/react-query'] = path.resolve(
        require.resolve('@tanstack/react-query'),
        '../../../'
      )
      return config
    }

@leo-petrucci
Copy link

I'm really confused. If I add @tanstack/react-query to my peer dependencies the only thing that happens is that I can't build my package anymore:

(typescript) Error: semantic error TS2307: Cannot find module '@tanstack/react-query' or its corresponding type declarations.
  "peerDependencies": {
    "@tanstack/react-query": "^4.22.0",
    "react": "18.x",
    "react-dom": "18.x"
  },

I've even tried passing context to my hooks but nothing, it's not working. I don't want people to have to install react-query in their own project just to use my package.

Is there really no way to fix this without custom webpack configs or weird workarounds?

@emurano
Copy link

emurano commented Feb 6, 2023

I don't want people to have to install react-query in their own project just to use my package.

Then you need to provide react-query as a dependency instead of a peer dependency. Peer dependencies are a declaration that the module using your module must have the dependency installed in order to use your module.

@leo-petrucci
Copy link

I don't want people to have to install react-query in their own project just to use my package.

Then you need to provide react-query as a dependency instead of a peer dependency. Peer dependencies are a declaration that the module using your module must have the dependency installed in order to use your module.

I'm aware of that, but if I do that my package crashes when it's used because of the issue mentioned in the OP.

@rwieruch
Copy link

rwieruch commented Feb 13, 2023

Just for the purpose of documenting it somewhere: We got the Error: No QueryClient set, use QueryClientProvider to set one in a project too -- which happened because we had two react-query installations.

The setup with the problem:

- app/
--- package.json <- react-query installation
--- shared-library/
------ docs/
--------- package.json <- react-query installation
------ packages/
--------- a/
------------ package.json
--------- b/
------------ package.json
--------- c/
------------ package.json

The architecture used a git submodule to nest a shared library into various applications. Because of this issue here, we restructured the whole architecture to a monorepo structure to have the react-query installations not nested but side by side instead -- which causes a shared yarn.lock file in the end and a more robust dependency resolution.

The new setup without the problem:

- shared-library/ <- monorepo
--- app/
------ package.json <- react-query installation
--- docs/
------ package.json <- react-query installation
--- packages/
------ a/
--------- package.json
------ b/
--------- package.json
------ c/
--------- package.json

@minisaw
Copy link

minisaw commented Apr 11, 2023

in a setup with several modules, make sure that all of them have their typescript compiler options (tsconfig.json) aligned in the following property:

"compilerOptions": {
"module": "esnext"
}

in case there is a mixture between "esnext" and "commonjs" module values, you will end up having several copies of the react-query library, leading to "Error: No QueryClient set, use QueryClientProvider to set one".

@bombillazo
Copy link

React-query V4 update:

webpack: (config, options) => {
      if (options.isServer) {
        config.externals = ['@tanstack/react-query', ...config.externals]
      }
      config.resolve.alias['@tanstack/react-query'] = path.resolve(
        require.resolve('@tanstack/react-query'),
        '../../../'
      )
      return config
    }

This worked for us! But since our next.config.js is at the root, we didn't have to add the path traversing.

@bertyhell
Copy link

bertyhell commented Apr 24, 2023

Mine worked like this, so you don't have to guess how many levels up you have to go if the nextjs is the root:

webpack: (config, options) => {
	// Fix issues with react-query:
	if (options.isServer) {
		config.externals = ['@tanstack/react-query', ...config.externals];
	}
	config.resolve.alias['@tanstack/react-query'] = path.resolve(
		'./node_modules/@tanstack/react-query'
	);

	return config;
}

@donalnofrixion
Copy link

For anyone else having this problem when using Vite.

You need to add @tanstack/react-query to the vite.config.ts rollupOptions

rollupOptions: { external: ['react', 'react-dom', 'axios', '@tanstack/react-query'],

@tqhoughton
Copy link

I appreciate that this isn't much to go on, but in general, what is the recommended method for providing custom hooks based on react-query in a library in terms of dependencies and configuration of the query client?

if you want a shared scope, meaning that the library uses the same context / client as the application that includes it, I would do it like you've described (I think):

  • have the lib define react-query as peerDependency
  • have the app create the queryClient and the QueryClientProvider.

the useQuery calls in the lib should just pick up that client. If that's not the case, you likely have either two versions of react-query or two versions of react around.

if you want an isolated scope, I would take a look at the custom context feature for v4.

We have this exact setup and are still running into this No QueryClient set, use QueryClientProvider to set one issue with react-query v4. Figured that as long as the app that imports the component is wrapped in a QueryClientProvider it would be fine as you said but somehow it doesn't seem to be finding the right version of react-query from the lib component even though it has react-query and react as a peer dependency. Confirmed that there is only 1 version of @tanstack/react-query in the yarn.lock file in the app repo.

Is converting our lib component to export as an ESM module really the only option we have to fix this?

@adrianzielonka
Copy link

I appreciate that this isn't much to go on, but in general, what is the recommended method for providing custom hooks based on react-query in a library in terms of dependencies and configuration of the query client?

if you want a shared scope, meaning that the library uses the same context / client as the application that includes it, I would do it like you've described (I think):

  • have the lib define react-query as peerDependency
  • have the app create the queryClient and the QueryClientProvider.

the useQuery calls in the lib should just pick up that client. If that's not the case, you likely have either two versions of react-query or two versions of react around.
if you want an isolated scope, I would take a look at the custom context feature for v4.

We have this exact setup and are still running into this No QueryClient set, use QueryClientProvider to set one issue with react-query v4. Figured that as long as the app that imports the component is wrapped in a QueryClientProvider it would be fine as you said but somehow it doesn't seem to be finding the right version of react-query from the lib component even though it has react-query and react as a peer dependency. Confirmed that there is only 1 version of @tanstack/react-query in the yarn.lock file in the app repo.

Is converting our lib component to export as an ESM module really the only option we have to fix this?

See @donalnofrixion's answer above, it should solve your issue.

@austinlangdon
Copy link

Both our next.js (webpack) app and react (vite) app faced this issue within our monorepo.

Here were the solutions for both

Vite: add a resolve alias to vite.config.ts

resolve: {
  alias: {
    '@tanstack/react-query': path.resolve(__dirname, './node_modules/@tanstack/react-query')
  }
}

Next.js: add a resolve alias to next.config.ts

webpack: (config, { isServer }) => {
    if (isServer) {
      config.externals = ['@tanstack/react-query', ...config.externals];
    }
    config.resolve.alias['@tanstack/react-query'] = path.resolve(
      './node_modules/@tanstack/react-query'
    );

    return config;
  },

@navignaw
Copy link

navignaw commented Jul 26, 2024

Any thoughts on adding a better error message when this happens? Given the length of this thread it's clear that hundreds of developer hours have been lost debugging this. Here's a pass at a better message:

No QueryClient found. There are a few symptoms that can cause this:
- You are missing a QueryClientProvider at the root level of your app which specifies the queryClient: https://tanstack.com/query/latest/docs/framework/react/reference/QueryClientProvider
- You have multiple versions of `@tanstack/react-query` installed in `node_modules`. This is typically the case in monorepos (like pnpm or yarn workspaces) and can be checked with `npm list @tanstack/react-query` or `yarn why @tanstack/react-query`, or by inspecting your `node_modules` directories manually. To fix this...

I'm happy to make a PR/change into the code itself but my main blocker is what to put as the resolution suggestion 😅 There are numerous workarounds in this thread but it's not clear to me which is the "blessed" solution.

(If the solution is too complex to put in the error message, we could also link to a new documentation page and add some debugging steps there)

@tqhoughton
Copy link

See @donalnofrixion's answer above, it should solve your issue.

Checking back in on this thread, I ended up converting the exporting package to use ESM module exports instead of commonjs and that appears to have fixed it. The info on how to do this in vite was not relevant to me since this was just a standard npm package export/import I was doing. +1 for having better docs that explain what you may be running into when your query client provider and your component that consumes useQuery are not colocated.

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