I'm trying to implement a navigation bar with the logout option.
Here's the code for my functional component:
import React, { useState } from 'react';
import { Nav, NavDropdown } from 'react-bootstrap';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
function MainMenu(props) {
const user = useSelector(state => state.user);
const dispatch = useDispatch();
const navigate = useNavigate();
const handleLogout = () => {
dispatch({
type: 'user/logout'
});
navigate('/home');
};
const logedInMenu = (<NavDropdown title={user.name} id="basic-nav-dropdown">
<NavDropdown.Item href="/dashboard">Dashboard</NavDropdown.Item>
<NavDropdown.Item href="/upload">Upload new Meme</NavDropdown.Item>
<NavDropdown.Divider />
<NavDropdown.Item href="#" onClick={handleLogout}>Logout</NavDropdown.Item>
</NavDropdown>);
const publicMenu = (<Nav className='me-auto'>
<Nav.Link href="/login">Login</Nav.Link>
<Nav.Link href="/signup">Signup</Nav.Link>
</Nav>);
return (user ? logedInMenu : publicMenu);
}
export default MainMenu;
The problem I have is that when I try to use this component:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom'
import Home from './Home';
import About from './About';
import Contact from './Contact';
import Login from './Login';
import Signup from './Signup';
import Dashboard from './Dashboard';
import Upload from './Upload';
import { getUser, removeUserSession } from './Utils/Common';
import MainMenu from './MainMenu';
import { Navbar, Container, Nav } from 'react-bootstrap';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const mainMenu = MainMenu();
const user = getUser();
const userReducer = (state, action) => {
switch (action.type) {
case 'user/login':
return {
user: action.payload.user,
token: action.payload.token
}
case 'user/logout':
return {
user: null,
token: null
}
default:
return {
user: null,
token: null
}
}
};
let store = createStore(userReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
const routing = (
<Provider store={store}>
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand href="#home">Leeway Academy</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="/">Home</Nav.Link>
{mainMenu}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<BrowserRouter>
<div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route path="/dashboard" element={
<RequireAuth redirectTo="/login">
<Dashboard />
</RequireAuth>
} />
<Route path="/upload" element={
<RequireAuth redirectTo="/login">
<Upload />
</RequireAuth>
} />
</Routes>
</div>
</BrowserRouter>
</Provider>
)
function RequireAuth({ children, redirectTo }) {
return getUser() ? children : <Navigate to={redirectTo} />;
}
ReactDOM.render(routing, document.getElementById('root'));
I get this error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- 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 don't understand why this is happening. I put together the login component using a seemingly similar approach and didn't have any problem.
Could this mean React is not recognizing my function as a functional component?
CodePudding user response:
const mainMenu = MainMenu();
You're calling MainMenu
as a regular function when the module loads, and not as a component inside the React tree.
You need to get rid of that and use <MainMenu />
where you have {mainMenu}