Component is missing after rerendering - reactjs

I want to create an SQL Editor. For that I need to display to which database I am currently connected to.
If I select some Database from the Dropdown Menu the Dropdown component will disappear completely.
Btw: I use the bootstrap library for Dropdown Menus
Here is what I got so far:
App.js
import Header from "./components/Header";
import Switch from "react-bootstrap/Switch";
import {Route, useParams} from "react-router";
import Navbar from "./components/Navbar";
import {useState} from "react";
import QueryPage from "./components/QueryPage";
import ChangeDB from "./components/essentials/ChangeDB";
function App() {
const [connections, setConnections] = useState([
{
id: 1,
dbname: "db1"
},
{
id: 2,
dbname: "db2"
},
{
id: 3,
dbname: "db3"
}]
);
const [db, setDB] = useState(1);
const onDBChange = ({id}) => {
setDB(() => id)
}
return (
<div className="App">
<Header database={db} connections={connections}/>
<Switch>
<Route exact path={["/", "/home","db/:id" ]}>
<QueryPage />
</Route>
<Route path="/db/:id"><ChangeDB callback={onDBChange} /></Route>
</Switch>
</div>
);
}
export default App;
Header.js
import {Button, Form, FormControl, Nav, Navbar, NavDropdown} from "react-bootstrap";
import "./Header.css";
import { Link } from 'react-router-dom';
function Header({ database , connections}) {
alert(database);
return (
<Navbar bg="light" expand="lg">
<Logo />
<Navbar.Brand href="#home">fnmSQL Client</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#link">Link</Nav.Link>
{connections.map(con => {
if(con.id === database) {
return (
<NavDropdown title={con.dbname} id="basic-nav-dropdown">
{connections.length !== 0 ?
connections.filter(id => (database !== id)).map(con => (
<NavDropdown.Item className={"bootstrap-overrides"} href={"#db/" + con.id}>{con.dbname}</NavDropdown.Item>
))
:
<NavDropdown.Item disabled="true">No Database Connections</NavDropdown.Item>
}
</NavDropdown>
)
}
})}
</Nav>
<Form inline>
<FormControl type="text" placeholder="Search" className="mr-sm-2" />
<Button variant="outline-success">Search</Button>
</Form>
</Navbar.Collapse>
</Navbar>
);
}
const Logo = () => {
return (
<Link to="./">
<img src={LogoWhite} alt={"ADVA Optical Networking SE"} className={"img"}/>
</Link>
)
}
export default Header;
I havent found another solution to read from HashRouter than putting it in some extra Component:
ChangeDB.js
function ChangeDB({callback}) {
let id = useParams();
callback(id);
return null;
}
export default ChangeDB;

Found a solution. It was because of the strict equality operator.
It seems that either database or con.id is parsed as a string instead of an integer.
I don't know why maybe somebody got an explanation...
Cheers guys

Related

React Context and React Custom hook with state not re-rendering after update

I am trying to toggle sidebar from navbar toggle button but unfortunately my custom react hook or react context is not triggering re render but the state is updating and also logging to the console but the it is not re rendering. I know this question is been asked so many times but I tried many things but couldn't able to work as expected.
Please help thankyou!
sidebar component -
import React from 'react';
import {useAuth} from '../contexts/auth';
import {useSider} from '../contexts/sider';
import Avatar from './Avatar';
import SideItem from './SideItem';
const Sider: React.FC = (): JSX.Element => {
const {auth} = useAuth();
const {expanded} = useSider();
return (
<>
<aside className='h-full border-r border-violet-200 bg-white'>
<ul className='p-1'>
<SideItem
to='#!'
key='avatar'
element={
<Avatar
username={auth?.user.username ?? 'username'}
src={auth?.user.avatar ?? undefined}
expanded={expanded}
/>
}
/>
<SideItem
to='/'
icon='fa-tachometer-alt'
title='Dashboard'
key='dashboard'
/>
<SideItem
to='/subscriptions'
icon='fa-file-export'
title='Subscriptions'
key='subscriptions'
/>
<SideItem
to='/sheets'
icon='fa-file-lines'
title='Sheets'
key='sheets'
/>
</ul>
</aside>
</>
);
};
export default React.memo(Sider);
Navbar component -
import React from 'react';
import {NavLink} from 'react-router-dom';
import {useAuth} from '../contexts/auth';
import {useSider} from '../contexts/sider';
import Brand from './Brand';
import NavItem from './NavItem';
const Nav: React.FC = (): JSX.Element => {
const auth = useAuth();
const {toggleSider} = useSider();
function signOut() {
// TODO : Sign out logic
}
function subscribe() {
// TODO : subscribe logic
}
return (
<>
<nav className='flex items-center justify-between px-6 py-1 bg-blue-50 border-b border-violet-200 sticky top-0'>
<button
className='border flex aspect-square p-1 text-violet-500'
onClick={() => toggleSider()}
>
<i className='fa-solid fa-bars' />
</button>
<NavLink to='/'>
<Brand />
</NavLink>
<ul className='w-full flex-grow justify-end flex items-center'>
{!auth ? (
<>
<NavItem to='/signin' title='Sign In' icon='fa-sign-in' />
<NavItem
to='/register'
variant='warning'
title='Register'
icon='fa-edit'
/>
</>
) : (
<>
<NavItem
to='#new-subscription'
onClick={subscribe}
variant='success'
title='New Subscription'
icon='fa-user-plus'
/>
<NavItem
to='#sign-out'
onClick={signOut}
variant='danger'
title='Sign Out'
icon='fa-sign-out'
/>
</>
)}
</ul>
</nav>
</>
);
};
export default React.memo(Nav);
SideItem component -
import React from 'react';
import {NavLink, NavLinkProps} from 'react-router-dom';
import {useSider} from '../contexts/sider';
interface SideItemProps extends NavLinkProps {
icon?: string;
element?: JSX.Element;
}
const SideItem: React.ExoticComponent<SideItemProps> = React.memo(
({to, title, icon, element, ...rest}) => {
const {expanded} = useSider();
return (
<li className='border-b border-violet-200 block text-violet-500 hover:text-violet-800 active:text-violet-500 p-1 lg:pl-0'>
{element ?? (
<NavLink
to={to}
{...rest}
className='flex p-2 lg:p-1 lg:pl-0 text-base lg:text-lg font-medium justify-center lg:justify-start items-center hover:bg-blue-50 active:bg-inherit lg:rounded-r-full rounded-md flex-col lg:flex-row'
>
<i className={'lg:mx-2 lg:w-5 fa-solid ' + icon}></i>
{expanded && <span className='hidden md:block'>{title}</span>}
</NavLink>
)}
</li>
);
}
);
export default SideItem;
App component -
import React from 'react';
import {Routes, Route} from 'react-router-dom';
import Nav from './components/Nav';
import Sider from './components/Sider';
import PrivateOutlet from './hoc/PrivateOutlet';
import Dashboard from './pages/dashboard';
import Register from './pages/register';
import Signin from './pages/signin';
import Subscriptions from './pages/subscriptions';
import Sheets from './pages/sheets';
import useSiderVisibility from './hooks/useSiderVisibility';
import {SiderProvider} from './contexts/sider';
const App: React.FC = (): JSX.Element => {
const showSider = useSiderVisibility();
return (
<>
<div className='App bg-zinc-100 overflow-hidden'>
<SiderProvider>
<Nav />
</SiderProvider>
<main className='flex h-full'>
{showSider && (
<div className='hidden sm:block relative p-2 pt-0 pl-0 lg:w-1/5 sm:w-1/6'>
<SiderProvider>
<Sider />
</SiderProvider>
</div>
)}
<div className='overflow-y-auto p-2 relative w-full'>
<Routes>
<Route path='/signin' element={<Signin />} />
<Route path='/register' element={<Register />} />
<Route path='/' element={<PrivateOutlet />}>
<Route index element={<Dashboard />} />
<Route path='subscriptions' element={<Subscriptions />} />
<Route path='sheets' element={<Sheets />} />
</Route>
<Route element={'404 Not Found'} />
</Routes>
</div>
</main>
</div>
</>
);
};
export default React.memo(App);
Sider context -
import React, {
createContext,
PropsWithChildren,
useCallback,
useContext,
useState,
} from 'react';
interface ISiderContext {
expanded: boolean;
toggleSider: () => void;
}
const initialValue = {
expanded: true,
toggleSider: () => {},
};
const SiderContext = createContext<ISiderContext>(initialValue);
/**
* A hook to get the information about whether the sider component is expanded or collapsed stored in Sider Context.
* #returns expanded: true if sider is expanded and toggleSider: a function toggle between expanded and collapsed.
*/
const useSider = () => {
return useContext(SiderContext);
};
const SiderProvider: React.ExoticComponent<PropsWithChildren> = React.memo(
({children}): JSX.Element => {
const [expanded, setExpanded] = useState<ISiderContext['expanded']>(
initialValue.expanded
);
const toggleSider = useCallback(() => {
setExpanded((prev) => !prev);
}, []);
return (
<SiderContext.Provider value={{expanded, toggleSider}}>
{children}
</SiderContext.Provider>
);
}
);
export {SiderProvider, useSider};
This was the original code in which i had tried different things but didn't work.
Thankyou!

react-bootstrap justify-content-end not being recognized when in desktop resolution, only when in mobile

I'm trying to mix the alignment of <Nav> items:
In desktop resolution, they should be justify-content-end.
In mobile resolution, they should be fill.
I can not get any alignment working in the desktop resolution, only in the mobile resolution.
No matter what I put the desktop resolution stays left aligned:
The mobile resolution is exactly how I want it:
So the questions are how can I mix the two and what am I doing wrong in my implementation?
// parent Navigation.tsx
// Bootstrap imports
import { Container, Navbar } from 'react-bootstrap';
// Component imports
import NavigationItems from './NavigationItems';
// Import data
import data from '../../data/data.json';
const Navigation = () => {
return (
<Navbar collapseOnSelect sticky='top' expand='lg'>
<Container>
<Navbar.Brand>{data.navbar.navBrand}</Navbar.Brand>
<Navbar.Toggle aria-controls='basic-navbar-nav' />
<Navbar.Collapse id='basic-navbar-nav' className='justify-content-end'>
<NavigationItems {...data.navbar.navItems} />
</Navbar.Collapse>
</Container>
</Navbar>
);
};
export default Navigation;
// child NavigationItems.tsx
// Bootstrap imports
import { Nav } from 'react-bootstrap';
const NavigationItems = (props: {name: string, href: string}[]): JSX.Element => (
<Nav fill className='me-auto'>
{
Object.entries(props).map((item, index) => {
return (
<Nav.Item key={index}>
<Nav.Link href={item[1].href}>{item[1].name}</Nav.Link>
</Nav.Item>
)
})
}
</Nav>
);
export default NavigationItems;
The <Navbar.Collapse id='basic-navbar-nav' className='justify-content-end'> came from the documentation example here.
I've tried <Nav className='me-auto justify-content-end'> but that only affects the alignment when in the mobile resolution.
This is what I ended up with that satisfies what I was after:
// Navigation.tsx
// Bootstrap imports
import { Container, Navbar } from 'react-bootstrap';
// Component imports
import NavigationItems from './NavigationItems';
// Declare types
type Navigation = {
navBrand: {
value: string,
href: string,
},
navItems: {
value: string,
href: string
}[]
}
// Component for the navbar element
const Navigation = (props: Navigation): JSX.Element => {
return (
<Navbar collapseOnSelect sticky='top' expand='lg' bg='white'>
<Container>
<Navbar.Brand href={props.navBrand.href}>{props.navBrand.value}</Navbar.Brand>
<Navbar.Toggle />
<Navbar.Collapse className='justify-content-end'>
<NavigationItems {...props.navItems} />
</Navbar.Collapse>
</Container>
</Navbar>
);
};
export default Navigation;
// NavigationItems.tsx
// Bootstrap imports
import { Nav } from 'react-bootstrap';
// Declare types
type NavigationItems = {
value: string,
href: string
}[]
// Component for the navbar items
const NavigationItems = (props: NavigationItems): JSX.Element => (
<Nav fill>
{
Object.entries(props).map((navItem, i) => {
return (
<Nav.Item key={i}>
<Nav.Link href={navItem[1].href}>{navItem[1].value}</Nav.Link>
</Nav.Item>
)
})
}
</Nav>
);
export default NavigationItems;

React Context: Sessionstorage data in developer tools appears but appears blank in navbar

I'm using React Context to store data into sessionstorage for the purposes of logging in and out. I'm saving three things: IDContext for saving the user's ID, UserNameContext for saving the user's username, and AdminContext to simply see if the logged in user is an admin or not. I want to display the logged in user's username on the navigation bar such as (Welcome, {user}) but instead it just displays as (Welcome, {}). I checked the sessionstorage via the developer tools, and it shows
username:""user""
So why exactly does it show up blank on the navigation bar, am I not grabbing the data correctly?
auth.js:
import { createContext, useContext } from "react";
export const UserNameContext = createContext();
export const IDContext = createContext();
export const AdminContext = createContext();
export function useUserName() {
return useContext(UserNameContext);
}
export function useID() {
return useContext(IDContext);
}
export function useAdmin() {
return useContext(AdminContext);
}
navbar.js:
import React from "react";
import { Navbar, Nav, Container } from "react-bootstrap";
import { Link } from "react-router-dom";
import { useID, useUserName, useAdmin } from "../context/auth";
function Navigation(props) {
const { IDTokens } = useID();
const { usernameTokens } = useUserName();
const { adminTokens } = useAdmin();
function adminNav() {
return (
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Link to="#">Placeholder</Link>
</Nav>
<Nav>
<Link to={`/${usernameTokens}`}>Welcome, {usernameTokens}</Link>
<Link to="/logout">Log Out</Link>
</Nav>
</Navbar.Collapse>
)
}
function loggedInNav() {
return (
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Link to="#">Placeholder</Link>
</Nav>
<Nav>
<Link to={`/${usernameTokens}`}>Welcome, {usernameTokens}</Link>
<Link to="/logout">Log Out</Link>
</Nav>
</Navbar.Collapse>
)
}
function guestNav() {
return(
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Link to="#">Placeholder</Link>
</Nav>
<Nav>
<Link to="/register">Register</Link>
<Link to="/login">Login</Link>
</Nav>
</Navbar.Collapse>
)
}
return (
<Navbar bg="primary" variant="dark" expand="md">
<Container>
<Link to="/">Flaskagram</Link>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
{IDTokens && adminTokens ? (
adminNav()
) : IDTokens ? (
loggedInNav()
) : (
guestNav()
)}
</Container>
</Navbar>
)
}
export default Navigation;
You don't need three separate contexts for storing those 3 pieces of information. To make your user object accessible from anywhere in your app, add this line inside App.js (at the beginning of the App function):
const [user, setUser] = useState();
Make sure the useState hook is imported in App.js, as well as UserContext from wherever you will define your context (in your case, I believe it is auth.js).
Then, wrap all the jsx elements that are returned from App.js with the context provider like so:
export default function App() {
const [user, setUser] = useState();
// any other code you might have here
return (
<UserContext.Provider value={{ user, setUser }}>
// all your app's components here
</UserContext.Provider>
);
}
In auth.js, this is all you need:
import React from "react";
const UserContext = React.createContext();
export default UserContext;
Wherever you set the user object (most likely on your login page), just use the following line to access the setUser function (and don't forget to import UserContext):
const { setUser } = useContext(UserContext);
Now, in navbar.js:
import React, { useContext } from "react";
import { Navbar, Nav, Container } from "react-bootstrap";
import { Link } from "react-router-dom";
import UserContext from "../context/auth";
function Navigation() {
const {
user: { username, isAdmin },
} = useContext(UserContext);
function adminNav() {
return (
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Link to="#">Placeholder</Link>
</Nav>
<Nav>
<Link to={`/${username}`}>Welcome, {username}</Link>
<Link to="/logout">Log Out</Link>
</Nav>
</Navbar.Collapse>
);
}
function loggedInNav() {
return (
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Link to="#">Placeholder</Link>
</Nav>
<Nav>
<Link to={`/${username}`}>Welcome, {username}</Link>
<Link to="/logout">Log Out</Link>
</Nav>
</Navbar.Collapse>
);
}
function guestNav() {
return (
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Link to="#">Placeholder</Link>
</Nav>
<Nav>
<Link to="/register">Register</Link>
<Link to="/login">Login</Link>
</Nav>
</Navbar.Collapse>
);
}
return (
<Navbar bg="primary" variant="dark" expand="md">
<Container>
<Link to="/">Flaskagram</Link>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
{username && isAdmin
? adminNav()
: username
? loggedInNav()
: guestNav()}
</Container>
</Navbar>
);
}
export default Navigation;
In order for this implentation to work, when you log the user in, the user object must have the properties of isAdmin and username set so that they can be accessed in the navbar. The isLoggedIn property is unnecessary because if the username property isn't null, it means that a user must be logged in.
Well, if you already have that item in your localstorage, you can just grab it using useEffect hook upon loading.
like this for example:
useEffect(() => {
const cachedUserName = JSON.parse(localStorage.getItem('username'));
if (cachedUserName ) {
setUserName(cachedUserName);
}
else {
...your logic
}
}, [setUserName]);

I want to Highlight the selected tab in Navbar on React BootStrap

I want to simply highlight the tab selected from the NavBar. I had used the Navbar Component of React-bootstrap and used the state to change the bgColor and textColor of the selected tab. But it is still NOT working? Any corrections?
import React,{useState} from 'react';
import { Navbar,Container, Nav } from "react-bootstrap"
import { LogOut } from "./LogOut"
const NavbarComponent = () => {
const [bgcolor, setBgcolor] = useState('black');
const [textcolor, setTextcolor] = useState('white');
function handleHighlightTab() {
setBgcolor('white');
setTextcolor('black');
}
return (
<>
<Navbar bg="dark" variant="dark" fixed='top' className='nav-pills'>
<Container>
<Navbar.Brand href="/">Verticals</Navbar.Brand>
<Nav className="me-auto">
<Nav.Link href="/grocery" onSelect={handleHighlightTab} style={{backgroundColor:{bgcolor},color:{textcolor} }} >Grocery</Nav.Link>
<Nav.Link href="/fashion" onSelect={handleHighlightTab} style={{backgroundColor:{bgcolor},color:{textcolor} }} >Fashion</Nav.Link>
<Nav.Link href="/footwear" onSelect={handleHighlightTab} style={{backgroundColor:{bgcolor},color:{textcolor} }}>Footwear</Nav.Link>
</Nav>
<LogOut />
</Container>
</Navbar>
</>
)
}
export default NavbarComponent;

React with Router v5 Error: Objects are not valid as a React child (found: object with keys {children})

I'm new to react but Im trying to create a web app that is essentially 2 apps in one. By 2 apps in one I mean I have 2 separate layouts, one when authorized and one when not. I'm running into a problem when logging in currently, when I try to redirect on successful log in I get the following error:
Error: Objects are not valid as a React child (found: object with keys {children}). If you meant to render a collection of children, use an array instead.
I'm sure this has something to do with how I have my routes set up or how Im redirecting.
Heres all my code. The error is usually pointing to the history.push line in login.js in the handlesubmit function. Note: my code is actually split into multiple js files for each function, I just combined them here so the code would be a bit more compact (I also combined the imports just for this example).
Update: I think I narrowed down my problem to my ProtectedRoute component, but I still dont totally know what the problem is. I think its how Im passing in an array of paths to that component but Im not sure how to fix it.
import React, { useState,useEffect } from "react";
import { NavLink, Route, Switch, useRouteMatch, useHistory, useLocation, useParams } from 'react-router-dom';
import MainLayout from "../layouts/MainLayout";
import AuthLayout from "../layouts/AuthLayout";
import NotFound from "../pages/NotFound";
import Login from "../pages/Login";
import Welcome from "../pages/Welcome";
import Dashboard from "../pages/Dashboard";
import Locations from "../pages/Locations";
import ProtectedRoute from "./ProtectedRoute";
import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import { LinkContainer } from "react-router-bootstrap";
import { ReactComponent as Logo } from '../images/Logo.svg';
import { useAppContext } from "../libs/contextLib";
import { LinkContainer } from "react-router-bootstrap";
import { useAppContext } from "../libs/contextLib";
import axios from 'axios';
import Form from "react-bootstrap/Form";
import LoaderButton from "../components/LoaderButton";
import LoginImage from '../images/Login-Page-Image.png';
import FloatLabelTextBox from "../components/FloatLabelTextBox.js"
import { API_BASE_URL, ACCESS_TOKEN_NAME } from '../constants/apiConstants.js';
import { onError } from "../libs/errorLib";
export default function MainRoutes() {
return (
<Switch>
<Route path={['/login', '/welcome']}>
<AuthLayout>
<Route path='/login' component={Login} />
<Route path='/welcome' component={Welcome} />
</AuthLayout>
</Route>
<ProtectedRoute exact path={['/', '/locations']}>
<MainLayout>
<Route path='/locations' component={Locations} />
<Route exact path='/' component={Dashboard} />
</MainLayout>
</ProtectedRoute>
{/* Finally, catch all unmatched routes */}
<Route>
<NotFound />
</Route>
</Switch>
);
}
function AuthLayout({children}) {
const { isAuthenticated } = useAppContext();
return (
<>
<div className="AuthLayout container py-3">
<Navbar collapseOnSelect expand="md" className="mb-3 login-nav">
<LinkContainer to="/welcome">
<Navbar.Brand href="/welcome" className="font-weight-bold text-muted">
<Logo />
</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Nav activeKey={window.location.pathname}>
<LinkContainer to="/welcome">
<Nav.Link>Home</Nav.Link>
</LinkContainer>
<LinkContainer to="/login">
<Nav.Link>Login</Nav.Link>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Navbar>
<div className="Auth-Layout-Body">
{children}
</div>
</div>
</>
);
}
export default AuthLayout;
function MainLayout({ children }) {
const { isAuthenticated } = useAppContext();
const { userHasAuthenticated } = useAppContext();
const history = useHistory();
function handleLogout() {
userHasAuthenticated(false);
console.log("log out");
history.push("/login");
}
return (
<>
<div className="MainLayout container py-3">
<Navbar collapseOnSelect expand="md" className="mb-3 login-nav">
<LinkContainer to="/">
<Navbar.Brand href="/" className="font-weight-bold text-muted">
Location INTEL
</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle />
<Navbar.Collapse className="justify-content-end">
<Nav activeKey={window.location.pathname}>
<LinkContainer to="/">
<Nav.Link>Home</Nav.Link>
</LinkContainer>
<LinkContainer to="/locations">
<Nav.Link>Locations</Nav.Link>
</LinkContainer>
{isAuthenticated ? (
<Nav.Link onClick={handleLogout}>Logout</Nav.Link>
) : (<div></div>)}
</Nav>
</Navbar.Collapse>
</Navbar>
<div className="Main-Layout-Body">
{children}
</div>
</div>
</>
);
}
export default MainLayout;
export default function Login() {
const history = useHistory();
const [state, setState] = useState({
email: "",
password: "",
});
const { userHasAuthenticated } = useAppContext();
const [isLoading, setIsLoading] = useState(false);
const handleChange = (e) => {
setState({
...state,
[e.target.name]: e.target.value,
})
}
function validateForm() {
return state.email.length > 0 && state.password.length > 0;
}
function handleSubmit(event) {
event.preventDefault();
setIsLoading(true);
const payload = {
"email": state.email,
"password": state.password,
}
try {
axios.post('/api/user/login', payload, {
headers: {
useCredentails: true,
'x-api-key': ACCESS_TOKEN_NAME,
"Access-Control-Allow-Origin": "*"
}
})
.then(function (response) {
console.log(response);
//console.log('status code = ' + response.status);
if (response.status === 200) {
console.log("logged in");
userHasAuthenticated(true);
history.push("/");
} else {
console.log("not logged in");
}
})
.catch(function (error) {
console.log(error);
});
} catch (e) {
onError(e);
setIsLoading(false);
}
}
return (
<div className="Login-Container">
<div className="Login-Container-Row">
<div className="Login">
<p className="Login-Header">Login</p>
<div className="Login-Form">
<Form onSubmit={handleSubmit}>
<Form.Group size="lg" controlId="email">
<FloatLabelTextBox
inputLabel="EMAIL"
inputAutoFocus="autofocus"
inputType="email"
inputName="email"
inputPlaceholder="Email"
inputValue={state.email}
handleChangeProps={handleChange}
/>
</Form.Group>
<Form.Group size="lg" controlId="password">
<FloatLabelTextBox
inputLabel="PASSWORD"
inputAutoFocus=""
inputType="password"
inputName="password"
inputPlaceholder="Password"
inputValue={state.password}
handleChangeProps={handleChange}
/>
</Form.Group>
<LoaderButton
block
size="lg"
type="submit"
isLoading={isLoading}
disabled={!validateForm()}>
Login
</LoaderButton>
<p>Not a member? <NavLink to="/register">Get Started Here</NavLink></p>
</Form>
</div>
</div>
<div className="Login-Image">
<img src={LoginImage} />
</div>
</div>
</div>
);
}
export default function ProtectedRoute({ children, ...props }) {
const { isAuthenticated } = useAppContext();
return (
<Route
{...props}
render={props => (
isAuthenticated ?
{children} :
<Redirect to='/login' />
)}
/>
);
}
Your problem is here:
<Route
{...props}
render={props => (
isAuthenticated ?
{children} : // <=== HERE!
<Redirect to='/login' />
)}
You are using {children} as if you were in "JSX-land" ... but you're not. You're already inside a set of {} in your JSX ... which means you're in "Javascript-land".
In "Javascript-land", {children} means "make me an object with a single child property called children". When you then try to insert that object into your JSX, React doesn't know what to do with it, and you get your error.
You just want the same code, minus those curly braces:
<Route
{...props}
render={props => (
isAuthenticated ?
children :
<Redirect to='/login' />
)}
P.S. Don't forget that route tags are just tags, so if you're doing this pattern a lot you might want to create an AuthenticatedRoute tag and use it (instead of repeating this ternary).

Resources