TanStack / query

🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.
https://tanstack.com/query
MIT License
41.7k stars 2.83k forks source link

No QueryClient set, use QueryClientProvider to set one #6432

Closed bogdan2510 closed 9 months ago

bogdan2510 commented 9 months ago

Describe the bug

I have an expo app inside nx.dev monorepo. all is good on dev but when i do a production build i get "No QueryClient set, use QueryClientProvider to set one".

main entry point is this:

import { useLocalStorage } from '/hooks';

import React from 'react'; import { QueryClient } from '@tanstack/react-query'; import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'; import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'; import * as Sentry from 'sentry-expo'; import Root from './Root';

Sentry.init({
  dsn: 'secret stuff here',
  enableInExpoDevelopment: true,
  debug: __DEV__,
});

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      gcTime: 1000 * 60 * 60 * 24, // 24 hours
    },
  },
});

const App = () => {
  const { clientPersisterStorage } = useLocalStorage();
  const clientPersister = createSyncStoragePersister({
    storage: clientPersisterStorage,
  });
  return (
    <PersistQueryClientProvider
      client={queryClient}
      persistOptions={{ persister: clientPersister }}
    >
      <Root />
    </PersistQueryClientProvider>
  );
};

export default React.memo(App);

Your minimal, reproducible example

This is only for productions builds

Steps to reproduce

This is happening only on production build. I don't have any ideea how to create a reproduction of this.

Expected behavior

To work

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

macOS

Tanstack Query adapter

react-query

TanStack Query version

5.7.2

TypeScript version

4.9.4

Additional context

Sentry is saying that error occoured in a custom hook that is inside the libs folder(im using nx monorepo.)

this is the hook:

import { Auth } from '/shared-types';
import jwtDecode from 'jwt-decode';
import { useCallback } from 'react';
import { useQuery } from '@tanstack/react-query';
import * as Sentry from 'sentry-expo';
import { useLocalStorage } from '../../index';

interface DecodedToken {
  exp?: number;
  [key: string]: unknown;
}

const useAuth = (): Auth => {
  const { storage } = useLocalStorage();

  const getLoginStatus = () => {
    const authToken = storage.getString('authToken');
    if (!authToken) {
      return false;
    }
    return true;
  };

  const { data: authStatus, refetch } = useQuery({
    queryKey: ['authStatus'],
    queryFn: getLoginStatus,
  });

  const decodeToken = useCallback(
    (token: string): boolean | null => {
      try {
        const decodedToken = jwtDecode<DecodedToken>(token);

        if (!decodedToken || !decodedToken.exp) {
          console.error(
            'Token is invalid or does not contain an expiration field.'
          );
          return null;
        }

        const currentTimestamp = Math.floor(Date.now() / 1000);
        if (decodedToken.exp < currentTimestamp) {
          console.error('Token has expired.');
          storage.delete('authToken');
          refetch();
          return null;
        }

        return true;
      } catch (err) {
        console.error('Error decoding token:', err);
        Sentry.Native.captureException(err);

        return null;
      }
    },
    [refetch, storage]
  );

  const login = useCallback(
    async (authToken: string) => {
      const isTokenValid = decodeToken(authToken);
      if (isTokenValid) {
        storage.set('authToken', authToken);
        refetch();
      } else {
        storage.delete('authToken');
        refetch();
      }
    },
    [decodeToken, refetch, storage]
  );

  const logout = () => {
    storage.delete('authToken');
    refetch();
  };

  return {
    login,
    logout,
    token: storage.getString('authToken'),
    authStatus,
    decodeToken,
  };
};

export default useAuth;
{
  "name": "/source",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "start": "nx run gsa:start --clear=true --skip-nx-cache=true",
    "start:fresh": "watchman watch-del-all && rm -rf node_modules/ && yarn cache clean && rm -rf .expo/ && yarn && nx sync-deps gsa --skip-nx-cache && nx ensure-symlink gsa --skip-nx-cache  && nx run gsa:start",
    "postinstall": "husky install"
  },
  "private": true,
  "dependencies": {
    "@config-plugins/react-native-blob-util": "^6.0.0",
    "@config-plugins/react-native-pdf": "^6.0.0",
    "@expo/metro-config": "0.7.1",
    "@formatjs/intl-datetimeformat": "^6.10.0",
    "@formatjs/intl-getcanonicallocales": "^2.2.1",
    "@formatjs/intl-locale": "^3.3.2",
    "@formatjs/intl-numberformat": "^8.7.0",
    "@formatjs/intl-pluralrules": "^5.2.4",
    "@gorhom/portal": "^1.0.14",
    "@nx/expo": "16.4.0",
    "@react-native-community/netinfo": "9.3.7",
    "@react-native-menu/menu": "^0.8.0",
    "@react-navigation/bottom-tabs": "^6.5.8",
    "@react-navigation/material-top-tabs": "^6.6.3",
    "@react-navigation/native": "^6.1.6",
    "@react-navigation/native-stack": "^6.9.12",
    "@sentry/react-native": "4.15.2",
    "@shopify/flash-list": "1.4.0",
    "@tanstack/query-sync-storage-persister": "5.7.2",
    "@tanstack/react-query": "5.7.2",
    "@tanstack/react-query-persist-client": "5.7.2",
    "expo": "48.0.19",
    "expo-application": "~5.1.1",
    "expo-build-properties": "~0.6.0",
    "expo-constants": "~14.2.1",
    "expo-dev-client": "~2.2.1",
    "expo-device": "~5.2.1",
    "expo-file-system": "~15.2.2",
    "expo-localization": "~14.1.1",
    "expo-location": "~15.1.1",
    "expo-splash-screen": "~0.18.2",
    "expo-status-bar": "~1.4.4",
    "expo-updates": "~0.16.4",
    "i18next": "^23.2.6",
    "jwt-decode": "^3.1.2",
    "ky": "^1.0.1",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-error-boundary": "^4.0.10",
    "react-hook-form": "^7.45.1",
    "react-i18next": "^13.0.1",
    "react-native": "0.71.8",
    "react-native-blob-util": "^0.19.1",
    "react-native-gesture-handler": "~2.9.0",
    "react-native-ios-context-menu": "^1.15.3",
    "react-native-mmkv": "^2.10.2",
    "react-native-pager-view": "6.1.2",
    "react-native-pdf": "^6.7.1",
    "react-native-reanimated": "~2.14.4",
    "react-native-safe-area-context": "4.5.0",
    "react-native-screens": "~3.20.0",
    "react-native-svg": "13.4.0",
    "react-native-svg-transformer": "1.0.0",
    "react-native-tab-view": "^3.5.2",
    "react-native-ui-lib": "^7.5.0",
    "react-native-web": "~0.18.12",
    "sentry-expo": "~6.2.0",
    "tslib": "^2.3.0",
    "zeego": "^1.6.2"
  },
  "devDependencies": {
    "@babel/preset-react": "^7.14.5",
    "@config-plugins/detox": "^6.0.0",
    "@expo/cli": "0.7.3",
    "@nx/detox": "16.4.0",
    "@nx/eslint-plugin": "16.4.0",
    "@nx/jest": "16.4.0",
    "@nx/js": "16.4.0",
    "@nx/linter": "16.4.0",
    "@nx/react": "16.4.0",
    "@nx/workspace": "16.4.0",
    "@react-native-community/eslint-config": "^3.2.0",
    "@testing-library/jest-dom": "5.16.5",
    "@testing-library/jest-native": "5.4.2",
    "@testing-library/react": "14.0.0",
    "@testing-library/react-native": "12.4.0",
    "@types/jest": "^29.4.0",
    "@types/node": "18.14.2",
    "@types/react": "18.0.28",
    "@types/react-dom": "~18.0.10",
    "@typescript-eslint/eslint-plugin": "^5.58.0",
    "@typescript-eslint/parser": "^5.58.0",
    "babel-jest": "^29.4.1",
    "babel-plugin-transform-remove-console": "^6.9.4",
    "babel-preset-expo": "~9.3.2",
    "detox": "^20.9.0",
    "eas-cli": "^5.9.1",
    "eslint": "^8.43.0",
    "eslint-config-prettier": "8.1.0",
    "eslint-plugin-import": "2.27.5",
    "eslint-plugin-jsx-a11y": "6.7.1",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-react": "7.32.2",
    "eslint-plugin-react-hooks": "4.6.0",
    "husky": "^8.0.0",
    "jest": "^29.4.1",
    "jest-circus": "^29.4.1",
    "jest-environment-jsdom": "^29.4.1",
    "jest-expo": "^48.0.2",
    "metro": "0.74.1",
    "metro-resolver": "0.74.1",
    "nx": "16.4.0",
    "prettier": "^2.8.8",
    "react-test-renderer": "18.2.0",
    "ts-jest": "^29.1.0",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.4"
  }
}

and this in the apps folder(i only have a expo app)

{
  "name": "gsa",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "@testing-library/jest-native": "*",
    "@testing-library/react-native": "*",
    "expo": "*",
    "react-native": "*",
    "react-native-reanimated": "*",
    "react-native-svg": "*",
    "react-native-ui-lib": "*",
    "react-native-web": "*",
    "@nx/expo": "*",
    "@expo/metro-config": "*",
    "react": "*",
    "@testing-library/react": "*",
    "@react-navigation/native": "*",
    "@react-navigation/native-stack": "*",
    "@react-navigation/bottom-tabs": "*",
    "react-hook-form": "*",
    "i18next": "*",
    "react-i18next": "*",
    "expo-localization": "*",
    "expo-splash-screen": "*",
    "@formatjs/intl-getcanonicallocales": "*",
    "@formatjs/intl-locale": "*",
    "@formatjs/intl-pluralrules": "*",
    "@formatjs/intl-numberformat": "*",
    "@formatjs/intl-datetimeformat": "*",
    "@react-navigation/material-top-tabs": "*",
    "expo-dev-client": "*",
    "expo-updates": "*",
    "expo-font": "*",
    "react-native-screens": "*",
    "react-native-gesture-handler": "*",
    "react-native-safe-area-context": "*",
    "react-native-pager-view": "*",
    "zeego": "*",
    "react-native-ios-context-menu": "*",
    "react-error-boundary": "*",
    "jwt-decode": "*",
    "expo-status-bar": "*",
    "@react-native-community/netinfo": "*",
    "expo-constants": "*",
    "ky": "*",
    "react-native-pdf": "*",
    "expo-file-system": "*",
    "@gorhom/portal": "*",
    "@shopify/flash-list": "*",
    "react-native-mmkv": "*",
    "expo-application": "*",
    "@sentry/react-native": "*",
    "sentry-expo": "*",
    "expo-device": "*",
    "@tanstack/react-query": "*",
    "@tanstack/react-query-persist-client": "*",
    "@tanstack/query-sync-storage-persister": "*"
  },
  "scripts": {
    "eas-build-pre-install": "cd ../../ && node tools/scripts/eas-build-pre-install.mjs . apps/gsa && cp yarn.lock apps/gsa",
    "eas-build-post-install": "cd ../../ && node tools/scripts/eas-build-post-install.mjs . apps/gsa"
  }
}
TkDodo commented 9 months ago

check your lock file. Almost always, you'll have two versions (or the same version twice) of @tanstack/react-query in there. also check node_modules if you have it there, twice. If you use pnpm, make sure you're on v8, because it had a resolution change regarding peer dependencies (see https://github.com/pnpm/pnpm/issues/5351)

with two versions of react-query, each one does createContext(), so they have different contexts, which means the consumer can't see the context of the Provider. So even though you have a QueryClientProvider, it will use a "different" react context than the consumer, so from the consumer side, there is no Provider.

I can't say more without seeing an actual reproduction for it. If my suggestion doesn't solve it, please feel free to re-open once you have a reproduction.

bogdan2510 commented 9 months ago

Hey. I don’t use trpc. I “solved” the problem by downgrading to version 5.0.0. :)

Mulțumesc , Bogdan Ioanas


From: Linus Nilsson Johnsen @.> Sent: Thursday, December 7, 2023 7:43:22 PM To: TanStack/query @.> Cc: Bogdan @.>; Mention @.> Subject: Re: [TanStack/query] No QueryClient set, use QueryClientProvider to set one (Issue #6432)

Hi @bogdan2510https://github.com/bogdan2510

I came across your issue since i am encountering a similar problem in my monorepo.

Given that this issue appears in production builds and considering your setup within a monorepo, I'm curious to know if you're using trpc or any other dependency that might indirectly utilize React Query in a different package inside your monorepo. This could potentially be a source of the problem, especially if there's some sort of conflict or misconfiguration between packages.

In my case, I'm also using a monorepo and wondering if there's a shared underlying cause. Have you explored this angle, or do you have any insights on whether such dependencies might affect the behavior of React Query in a production environment?

— Reply to this email directly, view it on GitHubhttps://github.com/TanStack/query/issues/6432#issuecomment-1845810614, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ABGAEDGDWFBYF2AZCSJWBBTYIH53VAVCNFSM6AAAAAA7ZEX54KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQNBVHAYTANRRGQ. You are receiving this because you were mentioned.Message ID: @.***>

4Furki4 commented 8 months ago

check your lock file. Almost always, you'll have two versions (or the same version twice) of @tanstack/react-query in there. also check node_modules if you have it there, twice. If you use pnpm, make sure you're on v8, because it had a resolution change regarding peer dependencies (see pnpm/pnpm#5351)

with two versions of react-query, each one does createContext(), so they have different contexts, which means the consumer can't see the context of the Provider. So even though you have a QueryClientProvider, it will use a "different" react context than the consumer, so from the consumer side, there is no Provider.

I can't say more without seeing an actual reproduction for it. If my suggestion doesn't solve it, please feel free to re-open once you have a reproduction.

I was having the same issue @bogdan2510 had, npm i resolved my package-lock.json file and the error has gone!

bogdan2510 commented 8 months ago

i'm in a monorepo setup using nx.dev. i use yarn. i don't have duplicates of "@tanstack/react-query" in lock file. Now i experience the problem even in version 5.0.0. :(. @4Furki4 @TkDodo

ArjunAtlast commented 7 months ago

I am facing the same issue with @tanstack/react-query version 5.17.19

bogdan2510 commented 7 months ago

Are you using expo, nx.dev?

kamaldeepj commented 6 months ago

I'm facing this issue with Expo and Nx.dev

kallis312 commented 2 months ago

same here

miroslavakalman commented 2 months ago

i have the same problem :(

emzet93 commented 1 month ago

Did any of you guys managed to solve it?

antonyleme commented 1 month ago

I was having the same issue here with expo and turborepo.

I followed the guide in the expo docs for setting up the project for a monorepo that says to change my entry point in the package json for a index.js that they provide and an env for expo-router.

What was happening was that when I added the config they provide, the expo-router stopped working properly and the _layout was being ignored.

Because of that, the QueryProvider that was placed inside the root _layout wasn't being instantiated.

That was causing the issue.

What I did was just leave everything as it was before, with my package.json "main" pointed to the expo-router/entry:

"main": "expo-router/entry",

For what I can see everyone here that is having this issue is also using a monorepo. So probably y'all made that change in the package.json too. I believe that maybe this solution will also work for y'all.

vendramini commented 4 weeks ago

@antonyleme I've seen your PR here https://github.com/expo/expo/pull/30536 but it's not working for me. My setup is a nx project with expo application.

"nx": "19.6.0",
"@nx/expo": "19.6.0",
"expo": "~51.0.28",
"expo-router": "~3.5.23",
"@tanstack/react-query": "^5.51.23",
"@tanstack/react-query-devtools": "^5.51.23",

Everything is up to date. I had the index.js file importing the expo-router/enty and ReactQuery wasn't being setup. Then I changed to main: "expo-router/entry" at package.json and the problem still happening.

I'm unsure where the problem could be: react query, nx or expo-router.

The logs of the production apk: image

Also, when doing console.log(queryClient) it outputs {}.

Dev apk DOES work pretty well.

Did you guys have any success? How can I debug this situation? I'm down to cooperate with testing @TkDodo .