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

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

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 8
  • Comments: 47 (6 by maintainers)

Commits related to this issue

Most upvoted comments

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

@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 });

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
    }

@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:

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;
  }

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.

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.

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

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;

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

@rametta Your package.json need to declare exports. Take a look at how i do it here: https://github.com/ecyrbe/zodios/blob/main/package.json

@rametta here : https://github.com/TanStack/query/issues/4346

But i closed it since i found that the issue is not in tanstack query but in webpack+next . The only way to make it go away is for your library to export esm + cjs packages

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

Any updates here? Is this still an issue?

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

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'],

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;
}

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”.

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

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" }, }

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

@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