0

I have been banging my head against the desk since last night trying to figure out what is causing this warning in my AstroJS project. I am currently building an AstroJS project with React components and without making any significant changes my terminal started through these errors when I run it in dev:

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.

I have been working with React for a while now and I am pretty sure I know how to use Hooks in functional components but maybe I am just blind and missing something. I have also checked for mismatching versions of React and renderer by running npm ls react and I can see that the versions are matching up:

@example/minimal@0.0.1 /Users/austinspinazze/Desktop/Projects/Uncharted-Labs-V1
└─┬ @astrojs/react@1.0.0
  ├─┬ react-dom@18.2.0
  │ └── react@18.2.0 deduped
  └── react@18.2.0

So that does not seem to be the problem either. I am also pretty certain I do not have more than one copy of React in my Astro project but I am not sure how to check for this?

Below are the only two React components in the Astro project so far:

Navbar

import React, { useState } from 'react';

const Navbar = () => {
    const [mobileMenu, setMobileMenu] = useState(false);

    const menuToggle = () => {
        mobileMenu ? setMobileMenu(false) : setMobileMenu(true);
        console.log(mobileMenu);
    };

    return (
        <nav className={[' w-full mx-auto bg-green-white']}>
            <div className='flex items-center justify-between space-x-20 px-10'>
                <div className='z-30 '>
                    <img
                        src='/assets/Uncharted_Labs_1.png'
                        alt=''
                        id='logo'
                        className='max-h-24'
                    />
                </div>
                <div className='hidden items-center md:space-x-10  uppercase text-dark-green md:flex'>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        About
                    </a>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        Services
                    </a>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        Pricing
                    </a>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        Blog
                    </a>

                    <a
                        href='#'
                        className='px-8 py-2 border-2 border-dark-green rounded-lg shadow-md hover:text-green-white hover:bg-dark-green ease-in-out duration-200 font-bold'
                    >
                        Contact
                    </a>
                </div>
                {/* Hamburger Button */}
                <button
                    onClick={menuToggle}
                    className='space-y-2 md:hidden cursor-pointer'
                >
                    <span className='block h-0.5 w-8 animate-pulse bg-gray-600'></span>
                    <span className='block h-0.5 w-8 animate-pulse bg-gray-600'></span>
                    <span className='block h-0.5 w-8 animate-pulse bg-gray-600'></span>
                </button>
            </div>

            {/* Mobile menu */}
            {mobileMenu && (
                <div
                    id='menu'
                    className='absolute p-6 md:hidden rounded-b-lg bg-green-white left-6 right-6 top-30 z-100'
                >
                    <div className='flex flex-col items-center justify-center w-full space-y-6 font-bold text-dark-green rounded-sm'>
                        <a href='#' className='w-full text-center'>
                            About
                        </a>
                        <a href='#' className='w-full text-center'>
                            Services
                        </a>
                        <a href='#' className='w-full text-center'>
                            Pricing
                        </a>
                        <a
                            href='#'
                            className='w-full pt-6 border-t border-gray-400 text-center'
                        >
                            Blog
                        </a>
                        <a
                            href='#'
                            className='w-full py-3 text-center rounded-full bg-dark-green text-green-white font-bold'
                        >
                            Contact
                        </a>
                    </div>
                </div>
            )}
        </nav>
    );
};

export default Navbar;

Hero

import React from 'react';

import styles from './styles.module.css';

const Hero = () => {
    return (
        <div className='flex flex-col gap-8 justify-items-center items-center mx-auto xl:px-20 px-10 lg:grid lg:grid-cols-hero'>
            <div id='hero-text' className='max-w-xl'>
                <h5 className='text-dark-green-text text-base leading-8 font-epilogue tracking-wide my-8'>
                    UNCHARTED LABS
                </h5>
                <h1 className='text-green-white font-epilogue font-bold my-8 xl:text-7xl text-5xl'>
                    The Simple, Clean Design
                </h1>
                <p className='text-light-grey-text font-epilogue leading-8 my-8 opacity-[.67]'>
                    Agency provides a full service range including technical
                    skills, design, and business understanding.
                </p>
                <button className='bg-dark-green-text pt-4 pb-3  px-5 rounded-md font-epilogue text-light-grey-text drop-shadow-md mt-5 font-bold'>
                    START EXPLORING
                </button>
            </div>
            <div id='hero-img' className='max-w-xl lg:max-w-3xl'>
                <div className={`${styles.blob}`}>
                    <image src='/assets/Hero.png' className='max-w-[120%]' />
                </div>
            </div>
        </div>
    );
};

export default Hero;

Astro file that is importing and using the Navbar and Hero components

---
import Hero from "../components/Hero/index";
import "../styles/global.css";
import Navbar from "../components/Navbar/index";
---

<html lang="en" class="top-0">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>Uncharted Labs</title>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
            href="https://fonts.googleapis.com/css2?family=Epilogue:wght@400;700&display=swap"
            rel="stylesheet"
        />
    </head>

    <body class="bg-dark-green">
        <Navbar client:load />
        <main class="flex justify-center mt-20">
            <section
                class="flex mt-10 sm:items-center w-full h-screen sm:mt-0 lg:h-[80vh]"
            >
                <Hero />
            </section>
        </main>
    </body>
</html>
Austin S
  • 145
  • 1
  • 10
  • 1
    Could you show how you're using these two components? – AKX Aug 22 '22 at 16:31
  • It may not matter but this is bothering me. `import React, { useState } from 'react';` - take out that extra `, { useState } ` and have `useState` be `React.useState` and see if it matters? – Nikki9696 Aug 22 '22 at 16:35
  • 3
    @Nikki9696 why is that a problem? – ggorlen Aug 22 '22 at 16:36
  • I don't know if it is. It's just nagging my brain. Eh, it probably makes no difference. – Nikki9696 Aug 22 '22 at 16:36
  • 1
    Code seems fine, although I'd write `setMobileMenu(prev => !prev)` rather than a ternary. – ggorlen Aug 22 '22 at 16:38
  • Works fine for me. Are you sure this is where the error is coming from? https://codesandbox.io/s/compassionate-margulis-ptb0pc?file=/src/App.js – Dave Newton Aug 22 '22 at 16:42
  • 1
    If you think the error is in these hook calls, the issue might be related to how Navbar is used. – Evert Aug 22 '22 at 17:12
  • @AKX I added the Astro file that is importing and rendering the two components. Also, the project itself is actually running fine. There are no errors in the browser in browser console and everything is rendering just this warning is make me question everything in life – Austin S Aug 22 '22 at 17:47
  • @Dave Newton that is the wild part it actually seems to work fine and on my colleague's machine he has no errors even after pulling down the changes I had pushed – Austin S Aug 22 '22 at 17:50
  • @AustinS If it works on your colleague's machine, could you try the usual – nuke `node_modules` and your package manager's lockfiles (`package-lock.json` and/or `yarn.lock`) and reinstall the bits and pieces and see if that helps... – AKX Aug 22 '22 at 17:52
  • @Evert that is the part that I am most confused about as the warning points to three different potential issues but even after installing a React hook-specific linter it threw no errors on either of the components – Austin S Aug 22 '22 at 17:52
  • Linters aren't smart enough usually to analyze your entire call tree. This might not be about the use of the hook specifically in this component, but perhaps the component was called as a regular function in a non-react context. – Evert Aug 22 '22 at 17:58
  • @AKX I had tried that earlier and just tried it again to make sure for my sanity that wasn't the issue and still received the same warning. But I just removed everything from the Navbar component and had it return null and the warning went away. So apparently there is something that React does not like about Navbar but to me I don't see a single issue – Austin S Aug 22 '22 at 17:58
  • @Evert that's a good point I wonder how I could check for that? – Austin S Aug 22 '22 at 18:00
  • Did some more testing and it seems to be an issue with the way useState is being implemented that the warning is coming from. I have to be missing something really small and dumb! – Austin S Aug 22 '22 at 18:26

1 Answers1

4

So I found the issue but not the exact answer as to why this happening but apparently there is currently a bug in @astro/react that forces you to actually directly export a component like this:

export default function Navbar() {
    const [mobileMenu, setMobileMenu] = useState(false);

    const menuToggle = () => {
        mobileMenu ? setMobileMenu(false) : setMobileMenu(true);
        console.log(mobileMenu);
    };

    return (
        <nav className={[' w-full mx-auto bg-green-white']}>
            <div className='flex items-center justify-between space-x-20 px-10'>
                <div className='z-30 '>
                    <img
                        src='/assets/Uncharted_Labs_1.png'
                        alt=''
                        id='logo'
                        className='max-h-24'
                    />
                </div>
                <div className='hidden items-center md:space-x-10  uppercase text-dark-green md:flex'>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        About
                    </a>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        Services
                    </a>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        Pricing
                    </a>
                    <a
                        href='#'
                        className='tracking-widest hover:border-b-2 hover:border-b-dark-green hover:mb-[-2px] ease-in-out duration-200'
                    >
                        Blog
                    </a>

                    <a
                        href='#'
                        className='px-8 py-2 border-2 border-dark-green rounded-lg shadow-md hover:text-green-white hover:bg-dark-green ease-in-out duration-200 font-bold'
                    >
                        Contact
                    </a>
                </div>
                {/* Hamburger Button */}
                <button
                    onClick={menuToggle}
                    className='space-y-2 md:hidden cursor-pointer'
                >
                    <span className='block h-0.5 w-8 animate-pulse bg-gray-600'></span>
                    <span className='block h-0.5 w-8 animate-pulse bg-gray-600'></span>
                    <span className='block h-0.5 w-8 animate-pulse bg-gray-600'></span>
                </button>
            </div>

            {/* Mobile menu */}
            {mobileMenu && (
                <div
                    id='menu'
                    className='absolute p-6 md:hidden rounded-b-lg bg-green-white left-6 right-6 top-30 z-100'
                >
                    <div className='flex flex-col items-center justify-center w-full space-y-6 font-bold text-dark-green rounded-sm'>
                        <a href='#' className='w-full text-center'>
                            About
                        </a>
                        <a href='#' className='w-full text-center'>
                            Services
                        </a>
                        <a href='#' className='w-full text-center'>
                            Pricing
                        </a>
                        <a
                            href='#'
                            className='w-full pt-6 border-t border-gray-400 text-center'
                        >
                            Blog
                        </a>
                        <a
                            href='#'
                            className='w-full py-3 text-center rounded-full bg-dark-green text-green-white font-bold'
                        >
                            Contact
                        </a>
                    </div>
                </div>
            )}
        </nav>
    );
}

If you would like to read more about potentially why this happening and maybe want a fun PR to resolve you can check out this Github issue https://github.com/withastro/astro/issues/4220

Austin S
  • 145
  • 1
  • 10
  • Ah, I suspect that's because Astro (for some reason) needs component functions to be named, and `const Foo = () =>` isn't a named function. – AKX Aug 23 '22 at 05:04
  • I believe you are correct! Hopefully, they will get a fix soon because that can definitely throw some developers off and send them down the rabbit hole. – Austin S Aug 23 '22 at 20:34