#Invalid hook call [very beginner question]

106 messages · Page 1 of 1 (latest)

grand sentinel
#

Hi, I am learning to code in typescript with react. I have no background in this domain, I am coming from python programming in robotics. I am sure this is a really easy solve, but I am simply lost, there's a lot of new stuff here 😅

With this in mind, I was trying to developp a user interface to control a simulation. I am at the very early stage of setting up the menu. I wanted to use tabs for this. My goal was that each tab would redirect to another page, in order to make code more separate and clean, but I'm having trouble using hooks with router.

My TabMenu.tsx files looks like this:

import { useState } from "react";
import { useNavigate } from "react-router-dom";

interface TopMenuProps {
  tabName: string[];
}

var navigate = useNavigate();

const routeChange = (item: string) => {
  let path: string;
  if (item === "Home") {
    path = "page";
  } else {
    path = "./" + item + "/page";
  }
  navigate(path);
};

const TopMenu = ({ tabName }: TopMenuProps) => {
  const [selectedMenu, setSelectedMenu] = useState(-1);
  return (
    <div role="tablist" className="tabs tabs-lifted">
      {tabName.map((item, index) => (
        <a
          role="tab"
          className={
            selectedMenu === index
              ? "tab tab-active [--tab-bg:lightblue] [--tab-border-color:blue]"
              : "tab"
          }
          key={item}
          onClick={() => {
            setSelectedMenu(index);
            routeChange(item);
          }}
        >
          {item}
        </a>
      ))}
    </div>
  );
};
export default TopMenu;

And my main page.tsx looks like this

"use client";
import TopMenu from "./components/TopMenu";
import React, { useState } from "react";

var menu = ["Home", "Simulation", "Parameter"];

function App() {
  return (
    <div>
      <TopMenu tabName={menu} />
    </div>
  );
}

export default App;

I am using React with Next.js, using daisyUI

My error is shown in the picture.

shrewd plover
#

you're just misusing hooks

grand sentinel
#

From what i've read in the link, I don't think my hook's usage is wrong, but rather, it has to do with duplicate of Reacts. Running npm ls react gives me this, I am guessing this is the issue, but I'm not sure, and I'm not sure why, as I didn't install random stuff

#

Oh

shrewd plover
#

hooks can only be called within components or other hooks

#

they're contextual and dynamic, so it doesn't make sense to use them at the top level

#

you'll probably want to move both navigate and routeChange inside TopMenu

#

also,

#

!:var

placid isleBOT
#
that_guy977#0
`!that_guy977:var`:

Don't use var in modern JS/TS applications! Its function scoping can lead to unexpected behavior. It is superceded by the block-scoped const and let. Read more

shrewd plover
#

use const here instead (for both navigate and menu)

grand sentinel
#
import { useState } from "react";
import { useNavigate } from "react-router-dom";

interface TopMenuProps {
  tabName: string[];
}

const TopMenu = ({ tabName }: TopMenuProps) => {
  const navigate = useNavigate();

  const routeChange = (item: string) => {
    let path: string;
    if (item === "Home") {
      path = "page";
    } else {
      path = "./" + item + "/page";
    }
    navigate(path);
  };
  const [selectedMenu, setSelectedMenu] = useState(-1);
  return (
    <div role="tablist" className="tabs tabs-lifted">
      {tabName.map((item, index) => (
        <a
          role="tab"
          className={
            selectedMenu === index
              ? "tab tab-active [--tab-bg:lightblue] [--tab-border-color:blue]"
              : "tab"
          }
          key={item}
          onClick={() => {
            setSelectedMenu(index);
            routeChange(item);
          }}
        >
          {item}
        </a>
      ))}
    </div>
  );
};
export default TopMenu;

You mean like this ?

shrewd plover
#

they're all the same copy of react

grand sentinel
#

Okay, thanks for the clarification !

shrewd plover
#

or if react and react-dom had different versions, though that's specific to react's usage

grand sentinel
#

Thanks!
i've moved the hooks as shown in the code above, but it still doesn't work. Now I get

shrewd plover
#

ok, so useNavigate is from react-router-dom, but it doesn't handle navigation itself fully
it works with a Router (provided by the same package) to handle the navigation and render correctly

#

TopMenu would have to be inside a Router
there can be intermediate components, but it has to have a Router somewhere in its parent chain

#

you'll probably want the Router to encapsulate basically the rest of the app, the parts that are affected by routing/navigation (perhaps excluding headers or footers, that's up to your design)

#

check react-router-dom docs

grand sentinel
#

so <Router></Router> at the top level of the return of my App ?

shrewd plover
#

sure, make sure to configure it though

grand sentinel
#

Ill take a look,
Also, Do i need to import it in everyfile that uses the Router? Or is there a way to import only once

shrewd plover
#

navigate tells the Router where to go
but you have to define to the Router what it should do for each path

shrewd plover
grand sentinel
shrewd plover
#

yeah you have to define the stuff you're using

grand sentinel
#

but if I don't in my other files, there's an error, and I'm wondering if there's an easy and clean way of importing it once, or if I should import it in every file that uses it

shrewd plover
#

using global stuff is possible, but it makes code harder to understand as it gets more complex

grand sentinel
#

okay thanks, ill import it everywhere then

shrewd plover
#

you shouldn't have many files with Routers though

#

1 for most uses, <4 if you want to get granular, i suppose

#

but from what you've shown, it's not super complex, so you should only need 1 Router

#

other files won't need to reference Router since they don't use it directly

grand sentinel
#

I have it 3 times, one for home, one for parameter and one for simulation, as they are all their own page.tsx

shrewd plover
#

do you have extra pages under each of those?

grand sentinel
#

Also, I am a bit confused, you mentionned the fact I need to configure my Route. I don't think I've done it, outside of the const routeChange, yet it works. Was this what you meant, or were you talking about something else?

#

not for now, no

#

only these 3

shrewd plover
#

then you only need 1 Router

#

the Router selects between those 3 pages

grand sentinel
#

Hmmm, maybe I didn't implement this correctly then 😅

#
import TopMenu from "../components/TopMenu";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

var menu = ["Home", "Simulation", "Parameter"];
const Parameter = (name: string) => {
  return (
    <div>
      <Router>
        <TopMenu tabName={menu} />
      </Router>
      {name}
    </div>
  );
};

export default Parameter;
```is my `/Parameter/page.tsx`, for instance. The simulation is the same, except for the name, and you already saw the main` page.tsx`
shrewd plover
#

yeah that's a lot of code duplication

#

you're calling TopMenu in each, you're defining menu again in each, that can lead to mismatches if you forget to update one of them

grand sentinel
#

I agree, but I figured I needed the menu to be in all 3 pages if I wanted a way to come back

shrewd plover
#

but they don't need to be defined in each page separately

#

put Router and TopMenu in the entry point instead, presumably App?

#

the Router and TopMenu aren't part of each page specifically, they're part of the App, so they should be called in the App

#

then you can define routes in the Router to say what slug corresponds to what component (each page)

grand sentinel
#

And they will stay visible regardless of what page I am on ?

shrewd plover
#

yeah

grand sentinel
#

hmmm okay, let me try

#

Okay it works, I don't understand hahaa, I thought the main page.tsx was just like a webpage in its own, but apparently not?

#

Also, apparently, if I let is sit idle for a bit, I get error 404, this page could not be found, Maybe my routes aren't 100% good?

shrewd plover
#

well, that's not good

#

no clue why thatd happen without any input

shrewd plover
grand sentinel
#

Also, if it helps, here is my folder architecture

#

I am actually starting to believe it doesn't work, because it doesn't render the content of the pages

#

Yeah okay, I can access the localhost:3000/home, but if I use the code, it puts this in the adress, and keeps the main App. If I go manually there, I can see its content, but not the top menu

shrewd plover
#

you might be linking your pages wrong, what does your entry point look like

grand sentinel
#
import TopMenu from "./components/TopMenu";
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";

var menu = ["home", "simulation", "parameter"];

function App() {
  return (
    <div>
      <Router>
        <TopMenu tabName={menu} />
      </Router>
    </div>
  );
}

export default App;
shrewd plover
#

you haven't configured the router, you need to pass Routes to define path => component relations

#

also you don't need that div layer

grand sentinel
#

I am terribly sorry, I ha to leave.
Thanks for the answers. Can I ask you for clarifications ? I am looking at this ,
Here, they make the parent route the path / with Layout
I am guessing this is the file where the main layout is locate. But in my case, Should I move my main layout(so the tabMenu) there, should I write simply TabMenu instead, or should I do something else?

#

Currently, I have ```typescript
"use client";
import TopMenu from "./components/TopMenu";
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";

import Home from "./Home/page";
import Simulation from "./Simulation/page";
import Parameter from "./Parameter/page";

var menu = ["home", "simulation", "parameter"];

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<TopMenu tabName={menu} />}>
<Route path="/home" element={<Home />} />
<Route path="/simulation" element={<Simulation />} />
<Route path="/parameter" element={<Parameter />} />
</Route>
</Routes>
</BrowserRouter>
);
}

export default App

#

My other files are simply like this:
/app/parameters/page.tsx

import React from "react";

const Parameter = (name: string) => {
  return (
    <div>
      TEST
      {name}
    </div>
  );
};

export default Parameter;
shrewd plover
#

i haven't worked with this in a while, but doesn't that only render TopMenu when the path is /?

grand sentinel
#

Hmm I don't really know. When I press home, I get the correct render, let me show you

shrewd plover
grand sentinel
#

Also, I dont have the last two lines that are in the links, so const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App />); I tried including them, but I get an error on the first line

#

And maybe that helps, but my TopMenu Is slightly different from the example,a s I don't use Link

import React from "react";
import { useState } from "react";
import { useNavigate, Outlet } from "react-router-dom";

interface TopMenuProps {
  tabName: string[];
}

const TopMenu = ({ tabName }: TopMenuProps) => {
  const navigate = useNavigate();

  const routeChange = (item: string) => {
    navigate("./" + item);
  };

  const [selectedMenu, setSelectedMenu] = useState(-1);
  return (
    <div role="tablist" className="tabs tabs-lifted">
      {tabName.map((item, index) => (
        <a
          role="tab"
          className={
            selectedMenu === index
              ? "tab tab-active [--tab-bg:lightblue] [--tab-border-color:blue]"
              : "tab"
          }
          key={item}
          onClick={() => {
            setSelectedMenu(index);
            routeChange(item);
          }}
        >
          {item}
        </a>
      ))}
      <Outlet />
    </div>
  );
};

export default TopMenu;
#

Ah, I found the issue ! I had forgotten than in my functions, I had a variable name name:string which i didn't submit. I removed it and now it works

#

I want to say, thank you so much. Coming from python and c++ from a long time ago, the transition is really hard, and you really helped me a lot, I truly appreciate it! Thank you for spending some time helping me 🫶

#

I want to ask one final little question before I close this post: apparently passing arguments inside a Route is slightly different, and I'm not sure I understand

#

For instance typescript <Route path="/" element={<TopMenu tabName={menu} />}> Seem to work perfectly fine
However,

<Route path="/parameter" element={<Parameter name={"test"} />} />```
doesn't. Do you know why? I've tried with and without brackets
shrewd plover
#

how doesn't it work exactly?

#

make sure you're destructuring name from props, and not accepting it as a parameter directly

shrewd plover
grand sentinel
#

Can you explain why I need to destructure it? I am passing a string, and expect a string on the other end, it feels redundant to destructure a string into a string, isn't it?

shrewd plover
#

you don't get a string on the other end, you get an object

#

otherwise you wouldn't be giving a name to the parameter

#

if you didn't get an object, you could have bugs like this

function C(x: string, y: number) {}
<C y={0}, x="" /> // swap!
```because js doesn't have named parameters, all parameters are positional
#

so jsx puts the parameters into an object, where order doesn't matter, names do

grand sentinel
#

Okay, thanks a lot ! That makes much more sense

#

I have a few other minor issues, but I'll try to tackle them on my own. Is it okay if I let this post open until I resolve them, in case I cannot find the answer?

shrewd plover
#

sure, but if they're not really related a new thread may work better

grand sentinel
#

It's still regarding that top menu's behavior. It's kind of the same topic as the last few messages, but not the original message I guess

#

For example: If I manually enter http://localhost:3000/parameter, the topMenu doesn't render, Or I would like to make home the default route. I managed to do that, but the tab isn't highlighted. These are the issue I'm looking at

shrewd plover
#

you'd want TopMenu outside of Routes

grand sentinel
#

But I thought The Route that encapsulates everything was the parent and would apply everywhere.
In that case, would you still have a Parent Route, or would it be outside Routes

shrewd plover
#

the Route encapsulates the part that's primarily affected by the route

grand sentinel
#

So I've tried this ```typescript
function App() {
return (
<BrowserRouter>
<TopMenu tabName={menu} />
<Routes>
<Route path="/">
<Route index element={<Home />} /> {/* Default Route */}
<Route path="/home" element={<Home />} />
<Route path="/parameter" element={<Parameter name="test" />} />
<Route path="/simulation" element={<Simulation />} />
</Route>
</Routes>
</BrowserRouter>
);
}


Still behaves the same
grand sentinel
#

I'm checking with chatGPT, and the issue could be the entry point. In a tutorial I was looking at, there was a main.tsx, but the guy was using React with Vite. I am using React with Next.js, and couldn't find the main.tsx or index.tsx. Could that be it? I am misusing the entry point?
I have gone with the assimption it was simply app/page.tsx

shrewd plover
#

im not familiar with next, so i can't answer that, sorry

#

just in general though, don't trust chatgpt

#

check next docs instead

grand sentinel
#

thanks a lot for the help! It is much appreciated !