简体   繁体   中英

React - Invalid Hook Call: convert App component to functional component, still get Invalid Hook Call


I have tried to create a simple app that allows a user to create or edit exiting 'projects'.

I am running in to the error:

react.development.js:1476 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

// at Routes (http://localhost:3000/static/js/bundle.js:154234:5) at App //

The code for my 'App.js' is:

    import React from "react";
    import { Routes, Route } from "react-router-dom";
    import MainPage from "./MainPage";
    //import LoginPage from "./LoginPage";
    //<Route path="/login" element={<LoginPage />} />;

    const App = () => {
      return (
        <Routes>
          <Route path="/projects" element={<MainPage />} />
        </Routes>
      );
    };

    export default App;

Nothing renders in the browser but the console throws the above error. I thought the error was related to using a functional component, but that doesn't seem to fix it (or more likely I'm too daft to figure out what I've done wrong).

I originally had the following code:

    import React from 'react';
    import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
    import MainPage from './MainPage';
    import LoginPage from './LoginPage';

    const App = () => (
      <Router>
        <Switch>
          <Route path="/login" component={LoginPage} />
          <Route path="/projects" component={MainPage} />
        </Switch>
      </Router>
    );

    export default App;

I realised 'switch' needed to be replaced with 'routes' and based on other online documentation used the 'elements' prop instaed of the component prop.

import React from 'react';
 import { Routes, Route } from 'react-router-dom';
 import MainPage from './MainPage';
import LoginPage from './LoginPage';

const App = () => {
 return (
     <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route path="/projects" element={<MainPage />} />
    </Routes>
  );
 };

export default App;`

Clearly this wan't the solution.

My MainPage.js code is:

import React, { useState, useEffect } from 'react';
import {
  Button,
  DataTable,
  TableContainer,
  Table,
  TableHead,
  TableRow,
  TableBody,
  TableCell,
} from 'carbon-components-react';

const MainPage = () => {
  // State for the list of projects
  const [projects, setProjects] = useState([]);

  // Fetch the list of projects from the server when the component mounts
  useEffect(() => {
    fetch('/api/projects')
      .then((res) => res.json())
      .then((data) => setProjects(data.projects));
  }, []);

  // Function to handle creating a new project
  const handleCreateProject = () => {
    // Display a form for the user to enter the project's name
        const projectName = window.prompt('Enter the name of the new project:');
       // If the user entered a name, create the new project and add it to the list
  if (projectName) {
    fetch('/api/projects', {
      method: 'POST',
      body: JSON.stringify({ name: projectName }),
      headers: { 'Content-Type': 'application/json' },
    })
      .then((res) => res.json())
      .then((data) => setProjects([...projects, data.project]));
  }
};

return (
  <div>
    <h1>My Projects</h1>
    <Button onClick={handleCreateProject}>Create New Project</Button>
    <TableContainer>
      <Table>
        <TableHead>
          <TableRow>
            <TableCell>Name</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {projects.map((project) => (
            <TableRow key={project.id}>
              <TableCell>{project.name}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  </div>
);
};

export default MainPage;

The LoginPage was just a placeholder (removed in the updated App.js).

My index.js code is:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

My package.json dependencies are:

 "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"

Update:

Updated App.js now using RouterProvider, createBrowserRouter. This is now throwing a browser console error:

Cannot read properties of undefined (reading 'map')

This seems to be coming from

return routes.map((route) => {

In the components.tsx file.

Current App.js code is:

    import React from "react";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import { Routes, Route } from "react-router-dom";
import MainPage from "./MainPage";
//import LoginPage from "./LoginPage";

const router = createBrowserRouter();

const routes = [
  { path: "/projects", element: <MainPage /> },
  //{ path: '/login', element: <LoginPage /> },
  // add more routes here
];

const App = () => {
  return (
    <RouterProvider router={router}>
      <Routes>
        {routes.map((route) => (
          <Route path={route.path} element={route.element} />
        ))}
      </Routes>
    </RouterProvider>
  );
};

export default App;

If you are using latest react-router-dom v6.4+ then you need to add RouterProvider in your root file and create router configuration using createBrowserRouter .

Update your index.js

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import MainPage from "./MainPage";
import LoginPage from "./LoginPage";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

const router = createBrowserRouter([
  {
    path: "/",
    element: <MainPage />
  },
  {
    path: "/login",
    element: <LoginPage />
  }
]);

root.render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>
);


The initial code:

import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import MainPage from './MainPage';
import LoginPage from './LoginPage';

const App = () => (
  <Router>
    <Switch>
      <Route path="/login" component={LoginPage} />
      <Route path="/projects" component={MainPage} />
    </Switch>
  </Router>
);

export default App;

When you updated from react-router-dom v5 to v6, ie replace the Switch component with the Routes component and changed the Route props, you appear to have dropped the Router component. A router is still required.

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import MainPage from './MainPage';
import LoginPage from './LoginPage';

const App = () => (
  <Router>
    <Routes>
      <Route path="/login" element={<LoginPage />} />
      <Route path="/projects" element={<MainPage />} />
    </routes>
  </Router>
);

export default App;

You shared your project's dependencies but I don't see react-router-dom listed as a project dependency. Ensure react-router-dom is actually installed and that the package.json file lists it as a dependency.

npm install --save react-router-dom@6

If you are trying to use the newer RRDv6.4+ Data APIs then you specify all the routes in the createBrowserRouter function. In your example it's not passed an array of routes, so this seems to be the cause of the mapping error. Pass the routes configuration array to createBrowserRouter . The RouterProvider also takes no children, it's self-closed.

import React from "react";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import { Routes, Route } from "react-router-dom";
import MainPage from "./MainPage";
import LoginPage from "./LoginPage";

// Routes configuration array
const routes = [
  { path: "/projects", element: <MainPage /> },
  { path: '/login', element: <LoginPage /> },
  // add more routes here
];

// Pass configuration array
const router = createBrowserRouter(routes);

const App = () => {
  return (
    <RouterProvider router={router} />
  );
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM