apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.34k stars 2.66k forks source link

Invalid hook call when used inside React Native #8525

Open Cretezy opened 3 years ago

Cretezy commented 3 years ago

Feels like I'm going crazy about this issue. I'm currently using @apollo/client with react-native in a yarn workspace. I've setup the yarn workspace correctly with the custom metro config required, and the nohoist options. Here are my configs:

 "workspaces": {
    "nohoist": [
      "@react-native-community/async-storage",
      "react-native",
      "react-native/**",
      "react-native-dev-menu",
      "react-native-svg",
      "jetifier",
      "react-native-gesture-handler",
      "@apollo/client",
      "react"
    ]
  },
  "resolutions": {
    "@types/react": "^17",
    "react": "17.0.2",
    "@apollo/client": "3.3.21"
  },
  "dependencies": {
    "react": "17.0.2",
    "@apollo/client": "3.3.21"
    // ...
  }
const getWorkspaces = require("get-yarn-workspaces");
const path = require("path");

function getConfig(appDir) {
  const workspaces = getWorkspaces(appDir);

  const watchFolders = [
    ...workspaces.filter((workspaceDir) => !(workspaceDir === appDir)),
    path.resolve(appDir, "node_modules"),
    path.resolve(appDir, "..", "node_modules"),
  ];

  return {
    watchFolders,
    resolver: {
      extraNodeModules: {
        "react-native": path.resolve(appDir, "node_modules", "react-native"),
        react: path.resolve(appDir, "node_modules", "react"),
        "react-native-svg": path.resolve(appDir, "node_modules", "react-native-svg"),
        "core-js": path.resolve(appDir, "node_modules", "core-js"),
        "@apollo/client": path.resolve(appDir, "node_modules", "@apollo", "client"),
      },
    },
  };
}

module.exports = getConfig(__dirname);

This works as expected, and normal React hooks works. But using useQuery gives me:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

This error is located at:
    in AppInner (at App.tsx:44)
    in ApolloProvider (at App.tsx:43)
    in App (at renderApplication.js:47)
    ...

My application code is:

import { AppRegistry } from "react-native";
import { App } from "./src/components/App";
import { name as appName } from "./app.json";
import "react-native-gesture-handler";

AppRegistry.registerComponent(appName, () => App);
import React, { useCallback, useContext, useEffect } from "react";
import { Text } from "react-native";
import {
  ApolloClient,
  ApolloProvider,
  useQuery,
} from "@apollo/client";
import { useQueryHookFromOtherPackage } "@propty/code/src/generated"

const client = new ApolloClient({
  // ...
});

export const App: React.FC = () => {
  return (
    <ApolloProvider client={client}>
      <AppInner />
    </ApolloProvider>
  );
};

const AppInner: React.FC = () => {
  // This hook is generated using GraphQL code generation in a package inside our yarn workspace
  useQueryHookFromOtherPackage()

  return <Text>test</Text>

It seems like it's specifically the useQuery hook that breaks, as other hooks there works fine.

I've checked and @apollo/client and react are also properly hoisted, meaning they should not be any weird version issues.

benjamn commented 3 years ago

@Cretezy Can you run npm ls react react-dom and share the output?

Alternatively/additionally, Apollo Client v3.4 is very close to release, and some aspects of importing/exporting React stuff have changed internally, so there's always a chance updating with

npm i @apollo/client@beta

could solve some problems.

Cretezy commented 3 years ago

@benjamn

➜  yarn why react           
yarn why v1.22.10
[1/4] Why do we have the module "react"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "react@17.0.2"
info Reasons this module exists
   - "_project_#@propty#frontends-mobile" depends on it
   - Hoisted from "_project_#@propty#frontends-mobile#react"
   - Hoisted from "_project_#@propty#frontends-web#react"
   - Hoisted from "_project_#@propty#frontends-storybook#react"
info Disk size without dependencies: "356KB"
info Disk size with unique dependencies: "404KB"
info Disk size with transitive dependencies: "432KB"
info Number of shared dependencies: 3
Done in 0.98s.

➜   yarn why react-dom      
yarn why v1.22.10
[1/4] Why do we have the module "react-dom"...?
[2/4] Initialising dependency graph...
[3/4] Finding dependency...
[4/4] Calculating file sizes...
=> Found "react-dom@17.0.2"
info Reasons this module exists
   - "_project_#@propty#frontends-web" depends on it
   - Hoisted from "_project_#@propty#frontends-web#react-dom"
   - Hoisted from "_project_#@propty#frontends-storybook#react-dom"
info Disk size without dependencies: "2.93MB"
info Disk size with unique dependencies: "3.18MB"
info Disk size with transitive dependencies: "3.21MB"
info Number of shared dependencies: 4
Done in 0.72s.

➜   npm ls react react-dom
@propty/frontends-mobile@1.0.0 /.../frontends/mobile
└── (empty)
Cretezy commented 3 years ago

@benjamn No luck with @apollo/client@3.4.0-rc.21 either

Cretezy commented 3 years ago

Okay narrowed it down a little.

My original code of using the following works:

useQuery(gql`
    query {
      me {
        id
      }
    }
  `);

However, using a generated hook from the GraphQL code gen, imported from a package in the workspace, does not. We have a package (@propty/core) in the workspace where the .graphql files are, and where the code gen happens. We import this package on our web/mobile packages. I've updated the OP with the "real" example (importing a generated hook)

I've tried unhoisting @apollo/client with no success.

Any ideas?

batical commented 3 years ago

having the same issue

Cretezy commented 3 years ago

I've found the source of the problem.

My current yarn workspace looks like:

Inside mobile/package.json, I have:

 "workspaces": {
    "nohoist": [
      "**/*"
    ]
  },

And in /package.json, I have:

  "workspaces": {
    "packages": [
      "web",
      "mobile",
      "core"
    ],
    "nohoist": [
      "**/react",
      "**/react/**",
      "**/@apollo/client",
      "**/@apollo/client/**"
    ]
  },

The problem

My core package has @apollo/client as a devDependency (mobile also has it). When I remove @apollo/client from the core's devDependency, I can use the hooks correctly.

This doesn't seem to be strictly a problem with @apollo/client, but more of a Metro/React Native issue.

No matter what I do, as long as @apollo/client is in the devDependencies, this error appears.

mangkoran commented 2 years ago

Currently using @apollo/client@3.5.6. Still getting the same error as OP.

MarcosSarges commented 1 month ago

I'm on version 3.11.4 and I'm still having this same problem.

When I try to use two mutations in the same component this occurs.

I feel like this is intermittent and related to hot reload

jerelmiller commented 1 month ago

@MarcosSarges hmmm if its related to hot reload, I'm not sure there is much we can do unfortunately :/. Let me add this to our next team meeting in case someone knows something I don't that might help here.

MarcosSarges commented 1 month ago

@MarcosSarges hmmm if its related to hot reload, I'm not sure there is much we can do unfortunately :/. Let me add this to our next team meeting in case someone knows something I don't that might help here.

I will try to investigate and try to bring more details.