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

Hooks + multiple instances of React #13991

Open
brunolemos opened this issue Oct 27, 2018 · 502 comments
Open

Hooks + multiple instances of React #13991

brunolemos opened this issue Oct 27, 2018 · 502 comments

Comments

@brunolemos
Copy link

brunolemos commented Oct 27, 2018

To people coming from search: please read this page first. It contains most common possible fixes!

Do you want to request a feature or report a bug?

Enhancement

What is the current behavior?

I had multiple instances of React by mistake.

When trying to use hooks, got this error:
hooks can only be called inside the body of a function component

Which is not correct since I was using function components. Took me a while to find the real cause of the issue.

What is the expected behavior?

Show the correct error message. Maybe detect that the app has multiple instances of React and say that it may be the reason of bugs.

@brunolemos brunolemos changed the title Wrong hooks error message when app has multiple instances of React Hooks + multiple instances of React Oct 27, 2018
@philipp-spiess
Copy link
Contributor

So just for clarification: You were importing a hook (say useState) from a different react module than the one used to render the component?

I agree that this is confusing. I'm not sure though if we have a way of knowing if any other React module is rendering. AFAIK we try to run React in isolation as much as possible so that multiple React instances can work in the same global context without issues.

Otherwise we could probably update the error message and mention this case as well if it's not too confusing.

@brunolemos
Copy link
Author

brunolemos commented Oct 27, 2018

Yes, I compared React1 === React2 and it was false (React1 being from index.js and React2 being from the file using the hook). When this happens, hooks fail with the generic error message above.

This issue is to raise awareness of this case and maybe improve the error message in some way to help people that face this. It's probably very edge though.

@GabrielBB
Copy link

GabrielBB commented Nov 1, 2018

Yup, i tried to npm link a package i'm creating. It throws that same error since the other package is also using hooks but with its own React. I had to publish my package to NPM and then import it directly from NPM. That way the error was gone, but i hope this is fixed since publishing a package without testing it is bad, obviously

@mpeyper
Copy link

mpeyper commented Nov 2, 2018

Lerna monorepos suffer from this as well when a custom hook is defined in one package and used by another as the symlinked dependencies use their own copy of react.

I have a (hacky) workaround at the moment using npm-link-shared and a prestart npm script to essentially replace the one package's react dependency with a symlink to the other's, so they use the same instance.

"prestart": "npm-link-shared ./node_modules/<other package>/node_modules . react"

@apieceofbart
Copy link

I had the same issue and I resolved it by adding:

 alias: {
        react: path.resolve('./node_modules/react')
      }

to resolve property in webpack config of my main app.

It's was obviously my mistake of using two copies of React but I agree that it would be great if the error message was better. I think this is maybe similar to: #2402

@GabrielBB
Copy link

@mpeyper It works. Thanks

@jimbo
Copy link

jimbo commented Nov 18, 2018

@apieceofbart That worked for me. Thanks for the suggestion. 👍

@jbandi
Copy link

jbandi commented Dec 3, 2018

As I understand this problem arises when there are multiple copies of React in the same bundle.

Is this also a problem if two separate bundles with their own copies of React are bootstrapping their own React applications on separate dom elements, like described here: https://medium.jonasbandi.net/hosting-multiple-react-applications-on-the-same-document-c887df1a1fcd

I think the latter is a common "integration" pattern used for instance in the single-spa meta-framework (https://github.com/CanopyTax/single-spa).

@vpicone
Copy link

vpicone commented Dec 10, 2018

I'm also having this issue even with the exact same react versions, developing hooks to be published on their own is broken when using npm-link. Getting the same unhelpful hooks can only be called inside the body of a function component message. @apieceofbart's alias solution solved this for me. Thanks so much!

@dotlouis
Copy link

Same issue here when I npm link a package to my main application. I could not get babel-plugin-module-resolver working.
It says:
Could not find module './node_module/react'
This is annoying because it prevents me from testing my component locally before publishing it.

@doasync
Copy link

doasync commented Dec 22, 2018

I fixed my issue by removing the caret in "react": "^16.7.0-alpha.2"
Here is the full comment: #14454 (comment)

@pelotom
Copy link

pelotom commented Dec 22, 2018

I'm using Yarn, and fixed this by forcing resolution in my package.json:

  "resolutions": {
    "**/react": "16.7.0-alpha.2",
    "**/react-dom": "16.7.0-alpha.2"
  },

@leecade
Copy link

leecade commented Dec 24, 2018

Same here!!

@zbuttram
Copy link

zbuttram commented Feb 5, 2019

Just wanted to leave a note here for anyone who might have had this problem in the same manner I did.

We're running React and Rails with the react-rails gem and rendering components directly into Rails views. I was receiving this error every time a new version of the app was pushed, because Turbolinks was grabbing the new JS bundle out of the <head> which loaded up an extra instance of React. Solution was to have Turbolinks do a full page reload when it detects the bundle has changed: https://github.com/turbolinks/turbolinks#reloading-when-assets-change

This appears to have solved it for us.

@taylorham
Copy link

taylorham commented Feb 6, 2019

I'm very excited to finally put Hooks into production, and we all owe a huge thank you to everyone who made it possible. They're a ton of fun to work with and have made my code shorter and more declarative.

Just as a heads up, this issue is still relevant in the released version with the same unhelpful error message of "Hooks can only be called inside the body of a function component."

Is this something that can be fixed? I imagine it might become more and more prevalent as more devs start to implement the new features, and a clearer error message would go a long way in lieu of an outright "fix".

Thanks again for all the hard work and congrats on the release! It's really an amazing set of features.

Edit: Should have looked closer at the open PRs, just found #14690 that addresses this. Thanks @threepointone!

@dotlouis
Copy link

dotlouis commented Feb 7, 2019

@taylorham The link in the commit doesn't point to anything yet. I'll wait for it, but this is an issue I have been having since using hooks in a linked (as of npm link) package and it's impossible to work with it locally without publishing.
After looking severals issues, I tought this was an issue with react-hot-loader that was compiling components to classes, but even after they released a version with Hook support, it still fails the same way.
I've tried a lot of different hacks but no luck. I don't know why everybody hasn't been struck with this issue yet 🧐

@drush
Copy link

drush commented Aug 19, 2023 via email

@J0EKW
Copy link

J0EKW commented Aug 23, 2023

This keeps being flared as an incorrect usage of the 'useState' hook, although it doesn't seem to fall into any of the usual pitfalls to my eyes. I've tried some different strategies for removing duplicate Reacts and there's no mismatch in version. The closest I can think is that the function it's used in is later used as a constant, but I've tried ways of removing the constant with the same results. Again, this might just be a goof.

import React, { useState } from 'react'
import { OptionsType } from '../types'

const Def = {
    animation: true,
}

function Option<T, >(normParam: T): {get: Function, set: Function, reset: Function} {
    const def: T = normParam;
    const [value, setValue] = useState<T>(def);


    const get = (): T => {
        return value
    }

    const set = (newValue: T): void => {
        console.log(newValue)
        setValue(newValue)
    }

    const reset = (): void => {
        setValue(def)
    }

    return {
        get: () => get(),
        set: (value: T) => set(value),
        reset: () => reset(),
    }
}

function Options(): OptionsType {
    const animation = Option<boolean>(Def.animation)

    return {
        animation: animation
    }
}

export default Options()

@snake-py
Copy link

snake-py commented Sep 1, 2023

Hello, I also have this issue. I read through the page carefully and checked everything and could not see anything amiss.

I have a custom component library in React 18.2.0 and a Project in React 18.2.0. Both use Vite for bundling. I am using serve side rendering with vite-plugin-ssr. I also asked the maintainer of VPS, but he said that it is a Dependency Injection issue of react-dom => vikejs/vike#1101

The issue is always occurring if I enable SSR, I don't see it if I only use client side rendering, which makes think that it might be due to that import ReactDOMServer from 'react-dom/server'; is somehow using another React then my library does.

In my library I have created a dummy hook:

function testHook() {
  console.log('testHook: testHook');
  console.log('testHook: React', React);
  console.log('testHook: ReactDOM', ReactDOM);
  console.log('testHook: testHook');
  const [testBool, setTestBool] = useState(false);
  useEffect(() => {}, []);
}

I am calling this hook in my top level page component:

function Page() {
  //@ts-ignore
  ReactDOM.testNew = 'testNew'; // I can see that this appended in my lib
  //@ts-ignore
  React.test = 'test';  // I can see that this appended in my lib
  const test = testHook();
  return <></>;
}

I also checked if react-dom is using the same React

./node_modules/react-dom/index.js

if (typeof window !== 'undefined') {
  window.React1 = require('react');
}

./page/dummy.tsx
import React from 'react';
import ReactDOM from 'react-dom';
...

  if (typeof window === 'undefined') {
    return <></>;
  }
  @ts-ignore
  window.React2 = require('react');
  @ts-ignore
  console.log(window.React1 === React); // true

So It seems to me that I am doing everything correctly and still see this issue.

I saw that the issue goes away if I do either:

  1. Use Client Side Rendering (not really an option)
  2. Redefine components and customHooks again in my project (not really an option)

Appendix:

I am unsure if this helps but here are also my Vite bundling configs of my lib:

/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import dts from 'vite-plugin-dts';

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      formats: ['es', 'cjs'],
      name: '@workdigtital/component-library-react',
      fileName: (format) => `index.${format}.js`
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM'
        },
        exports: 'named'
      }
    }
  },
  plugins: [react(), dts({ insertTypesEntry: true })],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/tests/setup.ts'],
    exclude: ['**/node_modules/**', '**/dist/**', '**/coverage/**', '**.stories**', '.storybook/**', '**.types**'],
    coverage: {
      all: true,
      exclude: ['**/*.stories.tsx', '.storybook/**'],
      provider: 'c8',
      reporter: ['cobertura', 'html']
    }
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@flaticon': path.resolve(__dirname, './node_modules/@flaticon')
    }
  }
});

Stack Trace:

react-dom.development.js:20662 Uncaught Error: Cannot read properties of null (reading 'useState')
    at updateDehydratedSuspenseComponent (react-dom.development.js:20662:17)
    at updateSuspenseComponent (react-dom.development.js:20362:16)
    at beginWork (react-dom.development.js:21624:14)
    at beginWork$1 (react-dom.development.js:27426:14)
    at performUnitOfWork (react-dom.development.js:26557:12)
    at workLoopSync (react-dom.development.js:26466:5)
    at renderRootSync (react-dom.development.js:26434:7)
    at performConcurrentWorkOnRoot (react-dom.development.js:25738:74)
    at workLoop (scheduler.development.js:266:34)
    at flushWork (scheduler.development.js:239:14)

Solution

My issue was with a third party plugin https://socket.dev/npm/package/vite-plugin-circular-dependency, removing it helped.

@hansede
Copy link

hansede commented Sep 13, 2023

None of the above solutions worked for me until I upgraded styled-components from v5 to v6. I was even getting true from React1 === React2 after using the Webpack alias solution, but was still seeing hundreds of these warnings.

If you're using styled-components, try upgrading.

@AjaykumarX
Copy link

I am getting "Invalid Hook Call" error for below code

export function GetMemoedData(param) {
     const data = useMemo(() => {
           return ['abcd']
     });
return data;
}

I am calling above function via Saga (yield call(GetMemoedData, params);

Any help on this would be appreciated

@danawan0409
Copy link

I'm getting Invalid hook call. I'm using this code from email.js:

`const Contact = () => {
const form = useRef;

const sendEmail = (e) => {
    e.preventDefault();
    
    emailjs.sendForm('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', form.current, 'YOUR_PUBLIC_KEY')
    .then((result) => {
        console.log(result.text);
    }, (error) => {
        console.log(error.text);
    });
};`

I'd appreciate the help

@jomefavourite
Copy link

😪😔

@tuseefahmed786
Copy link

I am creating a UI library so I used useState in my library component and when I use this library in my project with npm link library or npm install library I get this error

`reactjscomponents.js:2 Uncaught TypeError: Cannot read properties of null (reading 'useState')

` at t.useState (reactjscomponents.js:2:1)
at o (reactjscomponents.js:2:1)
at renderWithHooks (react-dom.development.js:16305:1)
at mountIndeterminateComponent (react-dom.development.js:20074:1)
at beginWork (react-dom.development.js:21587:1)
at beginWork$1 (react-dom.development.js:27426:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
at renderRootSync (react-dom.development.js:26434:1)
at recoverFromConcurrentError (react-dom.development.js:25850:1)

my package.json
{ "name": "@tuseefahmed110/al-kafeel-ui", "version": "1.0.4", "description": "we'r creating Pixel-Perfect UI Components library", "main": "dist/reactjscomponents.js", "scripts": { "build": "webpack", "test": "jest" }, "jest": { "setupFilesAfterEnv": [ "@testing-library/jest-dom/extend-expect" ] }, "keywords": [ "reactjs" ], "author": "Tuseef A. By AL-Kafeel Agency ", "license": "ISC", "dependencies": { "babel-plugin-transform-import-css": "^1.0.0-alpha.11", "react-dom": "file:../common/node_modules/react-dom", "react": "file:../common/node_modules/react" }, "devDependencies": { "@babel/cli": "^7.23.0", "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/preset-react": "^7.22.15", "@testing-library/jest-dom": "^6.1.4", "@testing-library/react": "^14.0.0", "babel-cli": "^6.26.0", "babel-loader": "^9.1.3", "chai": "^4.3.10", "css-loader": "^6.8.1", "jest": "^29.7.0", "mocha": "^10.2.0", "style-loader": "^3.3.3", "webpack": "^5.89.0", "webpack-cli": "^5.1.4", "sass": "^1.54.9", "sass-loader": "^13.0.2", "webpack-dev-server": "^4.11.0" } }

my webpack file
`const path = require("path");
const webpack = require("webpack");

module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /.(js|jsx|ts|tsx)$/,
loader: "babel-loader",
options: { presets: ["@babel/env"] }
},
{
test: /.(scss|css)$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
},
resolve: { extensions: ["*", ".js", ".jsx", ".tsx", ".ts"],
alias: {
react: path.resolve(path.join(__dirname, './node_modules/react')),
'react-dom': path.resolve(path.join(__dirname, './node_modules/react-dom'))
} },
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
externals: {
react: 'react',
'react-dom': 'react-dom'
},
devServer: {
allowedHosts: 'all',
open: true,
static: {
directory: path.join(__dirname, "public/"),
staticOptions: {},
publicPath: '/',
serveIndex: true,
watch: true,
},
compress: true,
historyApiFallback: true,
host: 'localhost',
port: 6600,
hot: true,
},
plugins: [new webpack.HotModuleReplacementPlugin()]
};`

my babel file
{ "presets": ["@babel/preset-env", "@babel/preset-react"] }

@mygithub07
Copy link

mygithub07 commented Nov 17, 2023

I am also getting Invalid hook call. Hooks can only be.. error . I started getting this when I changed my whole solution to use react-router-dom and introduced routing. Currently , on server, my app is working fine which does not have react-router-dom yet and has the same hooks logic as my local.

So I am guessing , it might have to do something with 1. mismatching versions of React and React DOM. or 2.more than one copy of React

. In general , react-router-dom and its compatibility with react . But at this point I am unbale to figure out
How can I resolve this error , please? when I run npm ls react-dom , I get

├─┬ @wordpress/scripts@26.6.0
│ ├─┬ @wordpress/babel-preset-default@7.20.0
│ │ └─┬ @wordpress/element@5.13.0
│ │   └── react-dom@18.2.0 deduped
│ └── react-dom@18.2.0
├─┬ rc-slider@10.3.1
│ ├─┬ rc-util@5.38.0
│ │ └── react-dom@18.2.0 deduped
│ └── react-dom@18.2.0 deduped
└─┬ react-router-dom@6.17.0
  └── react-dom@18.2.0 deduped

I am getting true for this check - window.React1 === window.React2 check

any help is appreciated

@AndreyEscobar
Copy link

do i have duplicate react?

projetofirestore@1.0.0 C:\APP\ProjetoFirestore
+-- @react-native-firebase/app@18.3.0
| -- react@18.2.0 deduped +-- @react-native-picker/picker@2.5.1 | -- react@18.2.0 deduped
+-- @react-navigation/drawer@6.6.4
| +-- @react-navigation/elements@1.3.19
| | -- react@18.2.0 deduped | +-- @react-navigation/native@6.1.7 | | +-- @react-navigation/core@6.4.9 | | | -- react@18.2.0 deduped
| | -- react@18.2.0 deduped | -- react@18.2.0 deduped
+-- @react-navigation/native-stack@6.9.13
| -- react@18.2.0 deduped +-- expo-camera@13.4.3 | -- @koale/useworker@4.0.2
| -- react@16.14.0 +-- expo-router@2.0.6 | +-- @radix-ui/react-slot@1.0.1 | | +-- @radix-ui/react-compose-refs@1.0.0 | | | -- react@18.2.0 deduped
| | -- react@18.2.0 deduped | +-- @react-navigation/bottom-tabs@6.5.8 | | -- react@18.2.0 deduped
| +-- expo-head@0.0.12
| | -- react@18.2.0 deduped | -- react-helmet-async@1.3.0
| -- react@18.2.0 deduped +-- pullstate@1.25.0 | -- react@18.2.0 deduped
+-- react-native-calendars@1.1302.0
| -- recyclerlistview@4.2.0 | -- react@18.2.0 deduped
+-- react-native-gesture-handler@2.12.1
| -- react@18.2.0 deduped +-- react-native-paper@5.11.1 | +-- @callstack/react-theme-provider@3.0.9 | | -- react@18.2.0 deduped
| +-- react@18.2.0 deduped
| -- use-latest-callback@0.1.6 | -- react@18.2.0 deduped
+-- react-native-reanimated@3.3.0
| -- react@18.2.0 deduped +-- react-native-safe-area-context@4.6.3 | -- react@18.2.0 deduped
+-- react-native-screens@3.22.1
| +-- react-freeze@1.0.3
| | -- react@18.2.0 deduped | -- react@18.2.0 deduped
+-- react-native-web@0.19.9
| +-- react-dom@18.2.0
| | -- react@18.2.0 deduped | -- react@18.2.0 deduped
+-- react-native@0.72.3
| +-- react-shallow-renderer@16.15.0
| | -- react@18.2.0 deduped | +-- react@18.2.0 deduped | -- use-sync-external-store@1.2.0
| -- react@18.2.0 deduped -- react@18.2.0

@mygithub07
Copy link

I am also getting Invalid hook call. Hooks can only be.. error . I started getting this when I changed my whole solution to use react-router-dom and introduced routing. Currently , on server, my app is working fine which does not have react-router-dom yet and has the same hooks logic as my local.

So I am guessing , it might have to do something with 1. mismatching versions of React and React DOM. or 2.more than one copy of React

. In general , react-router-dom and its compatibility with react . But at this point I am unbale to figure out How can I resolve this error , please? when I run npm ls react-dom , I get

├─┬ @wordpress/scripts@26.6.0
│ ├─┬ @wordpress/babel-preset-default@7.20.0
│ │ └─┬ @wordpress/element@5.13.0
│ │   └── react-dom@18.2.0 deduped
│ └── react-dom@18.2.0
├─┬ rc-slider@10.3.1
│ ├─┬ rc-util@5.38.0
│ │ └── react-dom@18.2.0 deduped
│ └── react-dom@18.2.0 deduped
└─┬ react-router-dom@6.17.0
  └── react-dom@18.2.0 deduped

I am getting true for this check - window.React1 === window.React2 check

any help is appreciated

This is resolved. The issue here was that I was using two ReactDOM.render in my solution one in index.js and other one in a component named RenderTopMenuResultPage , which I used only to render a different component. So it was creating two react trees somehow. I changed

export const RenderTopMenuResultPage = (props) => {

    ReactDOM.render(    
        <CreateTopMenuResultPage />, 
        document.getElementById('result-page-sort-menu')
    )
} 

to


export const RenderTopMenuResultPage = (props) => {
    return <CreateTopMenuResultPage />;
};

So the issue was never one of these which I was thinking - hooks rules , mismatching versions of React and React DOM , more than one copy of React Or using useNavigate() . They may be an issue in some scenarios but in this particular scenario the issue was using two ReactDOM.render

@craigkovatch
Copy link

craigkovatch commented Nov 21, 2023

I hit this error today on a component which renders a sub-component and attaches a callback ref to that sub-component. Inside the handler for that callback ref, it was calling a method which eventually called setState on the host component. This caused it to attempt to setState during an unmounting of the host because the callback ref handler wasn't checking the ref === null case.

Not sure if integral to the repro, but it also relied on accidentally double-rendering our entire dev page on page load. We were accidentally loading the entire webpack bundle twice, so the first render was getting unmounted and blown away by the second render.

It was quite hard to track down and didn't fit the three things listed in the troubleshooting error message. A big part of the difficulty was that the error was being reported from a hooks/function component which was being rendered as a sibling of the aforementioned host component, not a child.

@tuseefahmed786
Copy link

do i have duplicate react?

projetofirestore@1.0.0 C:\APP\ProjetoFirestore +-- @react-native-firebase/app@18.3.0 | -- react@18.2.0 deduped +-- @react-native-picker/picker@2.5.1 | -- react@18.2.0 deduped +-- @react-navigation/drawer@6.6.4 | +-- @react-navigation/elements@1.3.19 | | -- react@18.2.0 deduped | +-- @react-navigation/native@6.1.7 | | +-- @react-navigation/core@6.4.9 | | | -- react@18.2.0 deduped | | -- react@18.2.0 deduped | -- react@18.2.0 deduped +-- @react-navigation/native-stack@6.9.13 | -- react@18.2.0 deduped +-- expo-camera@13.4.3 | -- @koale/useworker@4.0.2 | -- react@16.14.0 +-- expo-router@2.0.6 | +-- @radix-ui/react-slot@1.0.1 | | +-- @radix-ui/react-compose-refs@1.0.0 | | | -- react@18.2.0 deduped | | -- react@18.2.0 deduped | +-- @react-navigation/bottom-tabs@6.5.8 | | -- react@18.2.0 deduped | +-- expo-head@0.0.12 | | -- react@18.2.0 deduped | -- react-helmet-async@1.3.0 | -- react@18.2.0 deduped +-- pullstate@1.25.0 | -- react@18.2.0 deduped +-- react-native-calendars@1.1302.0 | -- recyclerlistview@4.2.0 | -- react@18.2.0 deduped +-- react-native-gesture-handler@2.12.1 | -- react@18.2.0 deduped +-- react-native-paper@5.11.1 | +-- @callstack/react-theme-provider@3.0.9 | | -- react@18.2.0 deduped | +-- react@18.2.0 deduped | -- use-latest-callback@0.1.6 | -- react@18.2.0 deduped +-- react-native-reanimated@3.3.0 | -- react@18.2.0 deduped +-- react-native-safe-area-context@4.6.3 | -- react@18.2.0 deduped +-- react-native-screens@3.22.1 | +-- react-freeze@1.0.3 | | -- react@18.2.0 deduped | -- react@18.2.0 deduped +-- react-native-web@0.19.9 | +-- react-dom@18.2.0 | | -- react@18.2.0 deduped | -- react@18.2.0 deduped +-- react-native@0.72.3 | +-- react-shallow-renderer@16.15.0 | | -- react@18.2.0 deduped | +-- react@18.2.0 deduped | -- use-sync-external-store@1.2.0 | -- react@18.2.0 deduped -- react@18.2.0

I noticed I have duplicate react in my library How can remove ??

@chrisandrewca
Copy link

chrisandrewca commented Dec 22, 2023

Hello, I believe I have part of the solution for this error message. This error message can present itself in multiple ways depending on the exact APIs used in your component. I believe it all boils down to React detecting multiple copies of itself, even if they are the exact same version.

Issue(s):

Warning: 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.

TypeError: Cannot read properties of null (reading 'useState')

Solution:

  1. Define your dependencies. Which of your packages is the 'library' (frontend) and which is the 'consumer' (server)?
  2. Follow the guidance at the bottom of this page (Invalid Hook Call Warning - Duplicate React) Read the guidance for npm link. Once understood, navigate to your 'library' project and run: npm link ../path/to/server-consumer/node_modules/react and npm link ../path/to/server-consumer/node_modules/react-dom
  3. Next, navigate to the 'library' project's node_modules folder and run ls -la If you don't see this output (symlink arrows): react -> ../path/to/server-consumer/node_modules/react and react-dom -> ../path/to/server-consumer/node_modules/react-dom then proceed to the next step.
  4. Verify that react has been npm linked correctly with npm ls -g --depth=0 --link=true This is good for npm. Then follow the last step.
  5. Finally, we will manually setup the links. If you followed the steps above, you've verified your npm link setup, but ls -la in your 'library'/node_modules is still not showing symbolic link arrows (->) for both react and react-dom. Navigate to 'library'/node_modules and run rm -rf react, rm -rf react-dom, ln -s ../path/to/server-consumer/node_modules/react react, ln -s ../path/to/server-consumer/node_modules/react-dom

Run your app and hopefully the React error will be resolved!

Reproducing

This error can be reproduced with this minimal setup.
node v18.19.0
npm v10.2.3
react v18.2.0
react-dom v18.2.0

{
  "name": "frontend-package",
  "version": "1.0.0",
  "description": "frontend library for server which will SSR",
  "type": "module",
  "main": "./dist/esm/main.js",
  "module": "./dist/esm/main.js",
  "browser": "./dist/web/main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "peerDependencies": {
    "react": "18.2.0",
    "react-dom": "18.2.0"
  }
}
{
  "name": "server-consumer",
  "version": "1.0.0",
  "description": "SSR server-consumer of frontend-package",
  "type": "module",
  "main": "./src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "frontend-package": "1.0.0",
    "express": "4.18.2",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  }
}

I hope this helps some people out there. Thank you!

@WebGearMT
Copy link

WebGearMT commented Feb 19, 2024

Hi there fellow Dev's, I am struggling with this issue to, mine is related to plugin development.

I created two functional components for a plugin in WordPress: one called edit (used in the back-end for displaying the plugin in the block editor) and another called RecentPosts (which is supposed to be integrated into edit.js for more functionality).

Here's the code for edit:

import './editor.scss';
import { useEffect, useState } from "react";
import RecentPosts from './recentPosts.js';


/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {Element} Element to render.
 */
export default function Edit(props) {
        // The state variables to be used for dynamically retrieving and modifying data.
        const [resourceType, setResourceType] = useState('news');
        const [latestPost, setLatestPost] = useState(null);
        const [serverUrl, setServerUrl] = useState('http://lab.local/news');
		const [error, setError] = useState(null);
    
        // https://react.dev/reference/react/useEffect

        async function fetchFeaturedMediaUrl(featuredMediaId) {
            try {
                const response = await fetch(`http://lab.local/wp-json/wp/v2/media/${featuredMediaId}`);
                const mediaData = await response.json();
                return mediaData.source_url;
            } catch (error) {
                setError(error);
                console.error('Error fetching featured media:', error);
                return null;
            }
        }

        function trimText(text, char_count) {
            try {
                if (text.length > char_count) {
		            const trimmedText = text.slice(0, char_count) + '...';
                    
                    return trimmedText;
	            }
            } catch (error) {
                setError(error);
                console.error('Error retrieving the text: ', error);
            }
        }

    useEffect(() => {
        function fetchLatestPostData() {
            let index = 0; // Initialize index to start from 0

            const interval = setInterval(async () => {
                try {
                    const response = await fetch(`http://lab.local/wp-json/wp/v2/${resourceType}`, {
                        method: 'GET',
                        mode: 'cors'
                    });
                    const posts = await response.json();

                    // Reset index if it reaches the end
                    if (index >= posts.length) {
                        index = 0;
                    }
                
                    // Get the post at current index
                    const firstPost = posts[index++]; 

                    // Trim title if available
                    if (firstPost.title.rendered) {
                        trimText(firstPost.title.rendered, 37);
                    }
                
                    // Fetch and set featured media URL if available
                    if (firstPost 
                        && firstPost.featured_media 
                        && firstPost.excerpt.rendered 
                        && firstPost.title.rendered) {
                        const featuredMediaUrl = await fetchFeaturedMediaUrl(firstPost.featured_media);
                        setLatestPost({
                            link: firstPost.guid.rendered,
                            title: firstPost.title.rendered,
                            excerpt: firstPost.excerpt.rendered,
                            featuredMediaUrl: featuredMediaUrl
                        });
                    } else if (firstPost) { // No featured media, title or excerpt are available
                        setLatestPost({
                            link: firstPost.guid.rendered,
                            title: null,
                            excerpt: null,
                            featuredMediaUrl: null
                        });
                    }
                } catch (error) {
                    setError(error);
                    console.error('Error fetching the data:', error);
                }
            }, 5000); // Set interval to 5 seconds

            return () => clearInterval(interval); // Clear interval on component unmount
        }
        fetchLatestPostData()
    }, [resourceType, serverUrl]);

    if (!latestPost) {
        return <p>No new posts available. </p>;
    }

    // Below is the user interface.
	return(
		<div className="newsBlock-wrapper">
            <div className="newsBlock-container">
                <span className='carrot'><img className='arrow' src='arrow.svg' /></span>
                <div key={latestPost.id} className="latestContent">
                    {(() => {
                            if (latestPost.featuredMediaUrl) {
                                return (
                                    <img
                                    src={latestPost.featuredMediaUrl}
                                    alt={latestPost.title}
                                    />
                                );
                            }
                            // If there is no featured media URL, return null or an empty fragment
                            return null;
                        })()}
                    <div className='latestContentInner'>
                        <h3 className="latestTitle">{latestPost.title}</h3>
                        <p className="latest-excerpt">{latestPost.excerpt}</p>
                        <p className="read-more-link"><a href={latestPost.link}>Read More</a></p>
                    </div>    
                                 
                    <RecentPosts />
                    
                </div>
            </div>
        </div>
	);
}

Here's the code for RecentPosts:

import './editor.scss';
import { useEffect, useState } from "react";
import  fetchFeaturedMediaUrl  from "./edit.js";




export default function RecentPosts() {
    // State Variables
    const [resourceType, setResourceType] = useState('news');
    const [Posts, setPosts] = useState([]);
    const [serverUrl, setServerUrl] = useState('http://lab.local/news');
    const [error, setError] = useState(null);

    

    useEffect(() => {
        const controller = new AbortController();
        // const signal = controller.signal;

        async function fetchData() {
        /* Retrieve the latest post's data from the WordPress API using the Fetch API.*/

            try {
                const response = await fetch(`http://lab.local/wp-json/wp/v2/${resourceType}`, {
                method: 'GET',
                mode: 'cors'
            });
            const newsPosts = await response.json();
            //const newsPostsArray = Object.values(newsPosts);
            const firstFive = newsPosts.slice(0, 5);
            
            const postData = await Promise.all(firstFive.map(async (post) => {
                const featuredMediaUrl = post.featured_media ? await fetchFeaturedMediaUrl(post.featured_media) : null;
                return {
                    link: post.guid.rendered,
                    title: post.title.rendered,
                    excerpt: post.excerpt.rendered,
                    featuredMediaUrl: featuredMediaUrl
                }
            }));

            setPosts(postData);

            } catch(error) {
                setError(error);
                console.error('Error fetching the data:', error);
            } 
        }
        fetchData();
        return () => {
            controller.abort();
        }
    }, [resourceType, serverUrl]);

    return(
        <div className="other-recent-posts">
            {Posts.length > 0 && Posts.map((post, index) => (
                <div key={index} className="post">
                    <h3>{post.title}</h3>
                    {/* Render image if available */}
                    {post.featuredMediaUrl && (
                        <img
                            className="orp-thumbnail"
                            src={post.featuredMediaUrl}
                            alt={post.title}
                        />
                    )}
                    <p>{post.excerpt}</p>
                    {/* Add more fields as needed */}
                </div>
            ))}
        </div>
    );
}

After running my scripts using the command npm run start, I got error error in the console when visiting the block editor where I added the plugin, it said this:
Error fetching the data: Error: Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
Here's the full stack trace:

Error fetching the data: Error: Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    bt react-dom.min.js:10
    useState react.min.js:10
    Edit edit.js:37
    postData recentPosts.js:31
    fetchData recentPosts.js:30
    RecentPosts recentPosts.js:47
    Ur react-dom.min.js:10
    El react-dom.min.js:10
    xl react-dom.min.js:10
    v react.min.js:10
    ce react.min.js:10
    t react.min.js:10
    <anonymous> react.min.js:10
    <anonymous> react.min.js:10
[recentPosts.js:44:24](webpack://news-block/src/recentPosts.js)
    fetchData recentPosts.js:44
    RecentPosts recentPosts.js:47
    Ur react-dom.min.js:10
    El react-dom.min.js:10
    xl react-dom.min.js:10
    v react.min.js:10
    ce react.min.js:10
    (Async: EventHandlerNonNull)
    t react.min.js:10
    <anonymous> react.min.js:10
    <anonymous> react.min.js:10

To solve this problem, I clicked on the link provided in the error which brought me to this page. After clicking on the link provided in the red box, I got to this page. Over there, I found a list and analysis of possible causes of this error, I checked for all of them but it turned out that I didn't make any of the mistakes they referred to or violated the rules for React hooks. I also prompted ChatGPT (3.5), it gave me the following response:

The error you're encountering, "Minified React error #321," typically occurs when React detects that a function component is called outside of the body of a function component, such as during initial render or a re-render. This error usually indicates that there might be a violation of the rules of Hooks.

In your code, the issue might be related to the asynchronous nature of the fetchData function within the useEffect hook. When calling fetchData, it initiates an asynchronous operation without returning a cleanup function. This could lead to the error you're experiencing.

To address this issue, you can try the following adjustments:

Ensure that the fetchData function is called inside the useEffect hook to prevent it from being called outside the body of the component.

Consider adding a cleanup function inside the useEffect hook to handle any cleanup needed when the component unmounts or before re-rendering.

Here's how you can refactor your code:

javascript

useEffect(() => {
    async function fetchData() {
        try {
            const response = await fetch(`http://lab.local/wp-json/wp/v2/${resourceType}`, {
                method: 'GET',
                mode: 'cors'
            });
            const newsPosts = await response.json();
            const firstFive = newsPosts.slice(0, 5);
            
            const postData = await Promise.all(firstFive.map(async (post) => {
                const featuredMediaUrl = post.featured_media ? await fetchFeaturedMediaUrl(post.featured_media) : null;
                return {
                    link: post.guid.rendered,
                    title: post.title.rendered,
                    excerpt: post.excerpt.rendered,
                    featuredMediaUrl: featuredMediaUrl
                };
            }));

            setPosts(postData);

        } catch(error) {
            setError(error);
            console.error('Error fetching the data:', error);
        } 
    }

    fetchData();

    // Optionally, add a cleanup function here if needed

}, [resourceType, serverUrl]);

By ensuring that the fetchData function is called within the useEffect hook and handling any cleanup if necessary, you can mitigate the "Minified React error #321" issue.

As it seems to me, my code basically looks the same, as you'll see, fetchData() is also in the useEffect() function and there is a cleanup function for both these components.

I am not completely sure what could be causing this error. It only occurs When RecentPosts() is included in edit (i.e. import RecentPosts from './recentPosts.js'). When I comment the statements out, the error is gone. If any one has a solution to this problem it would be greatly appreciated.

@Abulkalam-Asif
Copy link

Abulkalam-Asif commented Apr 22, 2024

Please help me resolve this problem.
I am working on a Project in Next.js and have been facing this error and nothing is helping me resolve it.

Warning: 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.
 ⨯ TypeError: Cannot read properties of null (reading 'useContext')
    at ReadyProjectClientPage (./src/components/admin-side/ready-project/ReadyProjectClientPage.js:31:76)
digest: "3449185741"

Here is the ReadyProjectClientPage.js file mentioned in the error:

"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { AlertContext } from "@/context/AlertContext";
import { useContext } from "react";
// Other imports

const ReadyProjectClientPage = (props) => {
  const { showAlert } = useContext(AlertContext);
  const addReadyProjectS1Handler = async e => {
    e.preventDefault();
    // addReadyProjectS1Service is a function which runs on the client, checks the input before passing it to the Firebase server action function to add the data to database. No hook is called in this function, however, `showAlert()` returned by the `useContext(AlertContext)` is passed to this function to be called in case of invalid input.
    const data = await addReadyProjectS1Service(
      readyProjectS1,
      showAlert,
      setShowSpinner,
    );
  }
 // showAlert passed to other similar functions as well

  // Other code
  return (
    <>
      {/* JSX */}
    </>
  )
}

The main issue is that I am unable to reproduce the problem. The error appears randomly, once in a few hours, mostly when I reload the page. After it appears, no matter how hard may I try to reproduce it, it would not reappear.

I have manually checked all the hooks and they are all directly inside React components.

Here is my AlertContext.js file:

"use client";
import { createContext, useState } from "react";
const AlertContext = createContext({
  alerts: [],
  showAlert: () => {},
  hideAlert: () => {},
});

const AlertProvider = ({ children }) => {
  const [alerts, setAlerts] = useState([]);

  const showAlert = ({ type, message }) => {
    setAlerts([{ type, message, timestamp: Date.now() }]);
  };

  const hideAlert = () => {
    setAlerts([]);
  };

  return (
    <AlertContext.Provider value={{ alerts, showAlert, hideAlert }}>
      {children}
    </AlertContext.Provider>
  );
};

export { AlertProvider, AlertContext };

Here is my root layout.js file in which I have put the AlertProvider:

import { Roboto } from "next/font/google";
import "@/app/globals.css";
import { AlertProvider } from "@/context/AlertContext";
import StoreProvider from "@/store/StoreProvider";

const roboto = Roboto({
  subsets: ["latin"],
  weight: ["100", "300", "400", "500", "700", "900"],
});

export const metadata = {
  title: "title",
  description: "description",
};

export default function RootLayout({ children }) {
  return (
    <>
      <StoreProvider>
        <AlertProvider>
          <html lang="en">
            <body className={roboto.className}>{children}</body>
          </html>
        </AlertProvider>
      </StoreProvider>
    </>
  );
}

The versions of react and react-dom are both 18.
I visited the this link to React official site provided in the error description.
Under the Duplicate React section, it says:

If you use Node for package management, you can run this check in your project folder:
npm ls react
If you see more than one React, you’ll need to figure out why this happens and fix your dependency tree.

I ran the command on my terminal and here is the result:

PS C:\Users\user\Desktop\project> npm ls react
project@0.1.0 C:\Users\user\Desktop\project
+-- @reduxjs/toolkit@2.2.3
| `-- react@18.2.0 deduped
+-- next@14.2.1
| +-- react@18.2.0 deduped
| `-- styled-jsx@5.1.1
|   `-- react@18.2.0 deduped
+-- react-dom@18.2.0
| `-- react@18.2.0 deduped
+-- react-icons@5.1.0
| `-- react@18.2.0 deduped
+-- react-redux@9.1.1
| +-- react@18.2.0 deduped
| `-- use-sync-external-store@1.2.0
|   `-- react@18.2.0 deduped
+-- react-slick@0.30.2
| `-- react@18.2.0 deduped
`-- react@18.2.0

Then it says:

You can also try to debug this problem by adding some logs and restarting your development server:

// Add this in node_modules/react-dom/index.js
window.React1 = require('react');

// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2);

I tried it in my current Next.js project,in a new Next.js project, and in a new React.js project using vite.
I noticed that in any Next.js project, window.React1 pasted in node_modules/react-dom/index.js is not accessible in any component and returns undefined, so console.log(window.React1 === window.React2); always returns false. However in the React project, window.React1 pasted in node_modules/react-dom/index.js is accessible in React components and returns an object.

I have also added react and react-router-dom to peer-dependencies as mentioned in another answer.

{
  "name": "mehraz",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@reduxjs/toolkit": "^2.2.3",
    "firebase": "^10.11.0",
    "next": "14.2.1",
    "react-icons": "^5.1.0",
    "react-redux": "^9.1.1",
    "react-slick": "^0.30.2",
    "slick-carousel": "^1.8.1",
    "ulid": "^2.3.0"
  },
  "peerDependencies": {
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.19",
    "eslint": "^8",
    "eslint-config-next": "14.2.1",
    "postcss": "^8.4.38",
    "tailwindcss": "^3.4.3"
  }
}

@rwilliams3088
Copy link

rwilliams3088 commented Apr 30, 2024

I was experiencing this same issue when attempting to use yarn link during local development. @apieceofbart's answer was very helpful:

 alias: {
        react: path.resolve('./node_modules/react')
      }

However, I found this only partially fixed the issue. Because the project I was attempting to link was a react component library that in turn made use of various other react libraries (react-router, ag-grid, @mui/material, etc) I needed to add aliases for all these other dependencies as well for local development (for production, they are all marked as peer dependencies & externals). So my webpack configuration ended up looking more like this:

resolve: {
  alias: {
        "ag-grid-community": path.resolve(__dirname, "node_modules/ag-grid-community"),
        "ag-grid-enterprise": path.resolve(__dirname, "node_modules/ag-grid-enterprise"),
        "ag-grid-react": path.resolve(__dirname, "node_modules/ag-grid-react"),
        react: path.resolve(__dirname, 'node_modules/react'),
        "react-dom": path.resolve(__dirname, 'node_modules/react-dom'),
        "react-router": path.resolve(__dirname, "node_modules/react-router"),
        "react-router-dom": path.resolve(__dirname, "node_modules/react-router-dom"),
        "react-redux": path.resolve(__dirname, "node_modules/react-redux"),
        "@mui": path.resolve(__dirname, "node_modules/@mui")
      },
}

@sambaskar89
Copy link

react.development.js:209 Warning: 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.

how to resolve this error

@eithe
Copy link

eithe commented May 13, 2024

Sorry to disturb/notify all the followers of this issue, but for anyone coming here with a monorepo setup like npm workspaces, please make sure you don't have react included as dependencies multiple places, and also check any dangling package-lock.json or yarn.lock. I struggled with this issue for a couple of days, and in the end it all came down to a package-lock.json in one of the workspaces that had some react references that I didn't think of.

@desmati
Copy link

desmati commented Jun 4, 2024

If you are implementing a react component library and having this issue, try to include multiple versions in the peerDependencies.

"peerDependencies": {
"react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
},

@SanjidHaque
Copy link

I was developing a Shopify app and had the same issue.
In my case, I deleted the react folder from my_project\node_modules\@shopify\cli-kit\node_modules\react and re-run the app. It
re-created the react folder by its own and fixed the problem.

@Lonli-Lokli
Copy link

Is there way to find out in runtime that application has multiple React instances?

@KarlKeiser
Copy link

KarlKeiser commented Jul 24, 2024

Here's another way to get this error on Windows specifically. I had it in a Next.js project and in the end it was webpack that had a problem.

Anyway, the problem was that I used PowerShell and went to the project directory using commands like cd desktop. But the "Desktop" directory is capitalized! So what happens when you enter cd desktop? You get this:

image

Note how it's not capitalized. Windows is case insensitive and will find the directory anyway, and PowerShell doesn't care to check if the casing is right, so it just takes whatever you enter. Indeed, you can just use any casing that you want:

image

As you see, commands such as pwd will return the path with incorrect casing. Most of the time that doesn't matter anyway, but it seems like this time it mattered and webpack couldn't handle it, an that somehow caused this issue in React.

Solution

Ensure that the path has correct casing. When using commands like cd, pressing tab fixes such errors:

image

when pressing tab turns into

image

...which is then correct. Or install an "Open PowerShell here" to the right click context menu (can be done using regedit) to avoid having to navigate to your React project in PowerShell at all.

@syeddsherazi
Copy link

I have a react.js app which is added to our clients' websites as a widget. The clients' websites can be built on any tool e.g. wordress / angular / react etc.

We've two types of widgets search and chat. I'm using a single repo for both and generate two separate builds and host on aws s3.

On clients' sites we simply add a script and stylesheet of our build's index.js and index.css files hosted on s3. Till this part it works just fine e.g. Here's a sample build we've:
Screenshot 2024-07-25 at 3 46 00 PM

In my app we can have multiple grids which are to be lazy loaded based upon the client ( e.g. in the above build you can see the trips grid being lazy loaded ). We need this so that our build size doesn't increase much and on any individual client's site only the grids relevant to that client are loaded.

Now this works fine when I include either the search build or the chat build on clients' site. But when I include links to both the builds on clients site then I get a an error when lazy loaded components are loaded.

Here's an example of loader which is lazy loading trips grids:

import React, { Suspense, useMemo } from 'react'

// Utility function to lazy load the component
const getTripGridLazyLoaded = async organizationSlug => {
  switch (organizationSlug) {
    case 'clientA':
      return await import('./clientA/TripsGrid')
    case 'clientB':
      return await import('./clientB/TripsGrid')
    default:
      return await import('./common/TripsGrid')
  }
}

const TripsGridLoader = ({
  id,
  items,
  title,
  isLessThanLaptop,
  router,
  language,
  random_match,
  showRelevancyInfo,
  organizationSlug,
  itemsPerRow,
  itemsPerPage,
  isChatMode,
}) => {
  // Memoize the lazy-loaded component based on organizationSlug
  const TripGridLazyLoaded = useMemo(() => {
    if (!organizationSlug) return null
    return React.lazy(() => getTripGridLazyLoaded(organizationSlug))
  }, [organizationSlug])

  return (
    <>
      <Suspense fallback={<></>}>
        <TripGridLazyLoaded
          id={id}
          title={title}
          items={items}
          random_match={random_match}
          isLessThanLaptop={isLessThanLaptop}
          router={router}
          language={language}
          showRelevancyInfo={showRelevancyInfo}
          organizationSlug={organizationSlug}
          itemsPerRow={itemsPerRow}
          itemsPerPage={itemsPerPage}
          isChatMode={isChatMode}
        />
      </Suspense>
    </>
  )
}

export default TripsGridLoader

Here's a sample trip grid's code:

import React, { useState, useEffect } from 'react'
import { GridWrapper, ItemsWrapper } from './style'
import TripItem from './TripItem'
import { GridTitle, GridSubtitle, ShowMoreCta } from './style'
import { TRANSLATIONS_TEXTS } from '../../../../utils/constants'

const TripsGrid = ({
  id,
  items,
  title,
  isLessThanLaptop,
  router,
  language,
  random_match,
  showRelevancyInfo,
  itemsPerRow,
  itemsPerPage,
  isChatMode,
}) => {
  const [currentIsLessThanLaptop, setIsCurrentIsLessThanLaptop] =
    useState(false)
  const [itemsToShow, setItemsToShow] = useState(0)
  const [gridId, setGridId] = useState(null)

  useEffect(() => {
    if (isLessThanLaptop) setIsCurrentIsLessThanLaptop(isLessThanLaptop)
  }, [isLessThanLaptop])

  useEffect(() => {
    if (id !== gridId) {
      setGridId(id)
      setItemsToShow(itemsPerPage)
    }
  }, [id])

  if (!items?.length) return null

  const getItems = () => {
    const slicedItemsToShow = items.slice(0, itemsToShow)

    let itemsToReturn = slicedItemsToShow?.map((item, i) => {
      return (
        <TripItem
          key={item.id || item.url + item?.name}
          isLessThanLaptop={currentIsLessThanLaptop}
          router={router}
          item={item}
          language={language}
          showRelevancyInfo={showRelevancyInfo}
          randomMatch={random_match}
          itemsPerRow={itemsPerRow}
        />
      )
    })

    return itemsToReturn
  }

  if (getItems().length) {
    return (
      <GridWrapper id='grid-wrapper-trip'>
        <GridTitle $padding={'0px 15px 0px 0px'} isChatMode={isChatMode}>
          {title}
        </GridTitle>
        <ItemsWrapper id='grid-trip-items-wrapper'>{getItems()}</ItemsWrapper>
        {itemsToShow < items.length ? (
          <ShowMoreCta
            onClick={() => {
              setItemsToShow(itemsToShow + itemsPerPage)
            }}
          >
            {TRANSLATIONS_TEXTS.show_more[language]}
          </ShowMoreCta>
        ) : null}
      </GridWrapper>
    )
  } else return null
}

export default TripsGrid

I get the following error when trips grids ( or any other grids are lazy loaded - also these errors occur happen randomly not always )
Screenshot 2024-07-25 at 3 55 53 PM

I've looked into it and spent hours. As per the documentation here: https://react.dev/errors/321 ( it happens due to incorrect hook calls or react version mismatch errors ).

  • I've already checked that react version is the same on app load time and lazy loaded components' loading time by logging console.log(React.version)
  • Also the error not being there when just one of the two widgets search or chat is used is confusing.
  • Also I've added the code. It doesn't look like I have any incorrect hook call

If I do npm ls react to see if I've multiple versions of react in my app then I get a single version. Here's the output:

Screenshot 2024-07-25 at 4 08 40 PM

Note that it also works just fine when I don't lazy load trips grids ( or any other grids ) e.g. like this:

import React, { useMemo } from 'react'
import Common from './common/TripsGrid'
import ClientA from './clientA/TripsGrid'
import ClientB from './clientB/TripsGrid'

const TripsGridLoader = ({
  id,
  items,
  title,
  isLessThanLaptop,
  router,
  language,
  random_match,
  showRelevancyInfo,
  organizationSlug,
  itemsPerRow,
  itemsPerPage,
  isChatMode,
}) => {
  // Memoize the lazy-loaded component based on organizationSlug
  const TripGridLazyLoaded = useMemo(() => {
    if (!organizationSlug) return null
    switch (organizationSlug) {
      case 'clientA':
        return ClientA
      case 'clientB':
        return ClientB
      default:
        return Common
    }
  }, [organizationSlug])

  return (
    <>
      <TripGridLazyLoaded
        id={id}
        title={title}
        items={items}
        random_match={random_match}
        isLessThanLaptop={isLessThanLaptop}
        router={router}
        language={language}
        showRelevancyInfo={showRelevancyInfo}
        organizationSlug={organizationSlug}
        itemsPerRow={itemsPerRow}
        itemsPerPage={itemsPerPage}
        isChatMode={isChatMode}
      />
    </>
  )
}

export default TripsGridLoader

Can anyone please point me to the right direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests