ReferenceError: Worker is not defined while testing AppRouter Component - reactjs

I am testing a React/Typescript component using Jest and React Testing-Library. I am doing a simple render test and getting a Reference Error: Worker is not defined.
Why and how would I use a worker in this testing context?
Here is my simple test:
import {render, screen} from '#testing-library/react'
import userEvent from '#testing-library/user-event'
import React from 'react'
import {Router} from 'react-router-dom'
import AppRouter from '../router'
test('AppRouter renders all routes and I can navigate to those pages', () => {
render(<AppRouter />)
screen.debug()
})
And here is the AppRouter component:
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
import { useState } from 'react'
import useLocalStorage from './hooks/useLocalStorage'
import * as Constants from './constants'
import Header from './layout/header/header'
import MainPage from './pages/mainPage/mainPage'
import PostPage from './pages/postPage/postPage'
import UserPage from './pages/userPage/userPage'
import LoginPage from './pages/loginPage/loginPage'
import SignupPage from './pages/signupPage/signupPage'
import NewPage from './pages/newPage/newPage'
import FeedbackPage from './pages/feedbackPage/feedbackPage'
import AdminPage from './pages/adminPage/adminPage'
import SettingPage from './pages/settingPage/settingPage'
import { WebContext } from './context/WebContext'
import Favicon from 'react-favicon'
const AppRouter = () => {
const [adminCode, setAdminCode] = useLocalStorage('admin', '')
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [page, setPage] = useState(Constants.Page.Home)
return (
<BrowserRouter>
<div>
<Favicon url={require('../public/favicon.ico')} />
<WebContext.Provider
value={{
isMenuOpen,
setIsMenuOpen,
page,
setPage,
adminCode,
setAdminCode,
}}
>
<Header />
<Switch>
<Route component={MainPage} path="/" exact={true} />
<Route component={PostPage} path="/post/:id" />
<Route component={UserPage} path="/user" />
<Route component={LoginPage} path="/login" />
<Route component={SignupPage} path="/signup" />
<Route component={NewPage} path="/new" />
<Route component={FeedbackPage} path="/feedback" />
<Route component={AdminPage} path="/admin" />
<Route component={SettingPage} path="/setting" />
<Route component={() => <Redirect to="/" />} />
</Switch>
</WebContext.Provider>
</div>
</BrowserRouter>
)
}
export default AppRouter
Most of what I researched on this were old Jest stackoverflows. I am aware there is a jest-worker package but not sure why I would need this or how I would use it when running this simple test.
Here is a link to jest-worker.
Code Trace:
Header component
import { useContext, useState } from 'react'
import { NavLink, useHistory, useLocation } from 'react-router-dom'
import { observer } from 'mobx-react-lite'
import { WebContext } from '../../context/WebContext'
import UnirepContext from '../../context/Unirep'
import UserContext from '../../context/User'
const Header = () => {
const history = useHistory()
const location = useLocation()
const { isMenuOpen, setIsMenuOpen } = useContext(WebContext)
const [searchInput, setSearchInput] = useState<string>('')
const unirepConfig = useContext(UnirepContext)
const userContext = useContext(UserContext)
const gotoNewPage = () => {
if (
userContext.userState &&
userContext.netReputation >= unirepConfig.postReputation
) {
history.push(`/new`, { isConfirmed: true })
}
}
const gotoUserPage = () => {
history.push(`/user`, { isConfirmed: true })
}
const openMenu = () => {
if (!isMenuOpen) {
console.log('open menu!')
setIsMenuOpen(true)
}
}
const handleSearchInput = (event: any) => {
console.log('search input : ' + event.target.value)
}
return (
<header>
<div className="navLinks">
<NavLink to="/" className="link" activeClassName="active" exact>
<img
src={require('../../../public/images/unirep-title.svg')}
/>
</NavLink>
</div>
{/* <div className="search-bar">
<div className="search-icon"><FaSearch /></div>
<form>
<input type="text" name="searchInput" placeholder="Search by keyword, user names or epoch key" onChange={handleSearchInput} />
</form>
</div> */}
{userContext.userState ? (
<div className="navButtons">
<div id="rep" onClick={gotoUserPage}>
<img
src={require('../../../public/images/lighting.svg')}
/>
{userContext.netReputation}
</div>
<div
id="new"
className={
location.pathname === '/new'
? 'navBtn chosen'
: 'navBtn'
}
>
<img
src={require('../../../public/images/newpost.svg')}
onClick={gotoNewPage}
/>
</div>
<div
id="user"
className={
location.pathname === '/user'
? 'navBtn chosen'
: 'navBtn'
}
>
<img
src={require('../../../public/images/user.svg')}
onClick={gotoUserPage}
/>
</div>
<div className="navBtn">
<img
src={require('../../../public/images/menu.svg')}
onClick={openMenu}
/>
</div>
</div>
) : (
<div className="navButtons">
<div
id="login"
className="whiteButton"
onClick={() => history.push('/login')}
>
Sign in
</div>
<div
id="join"
className="blackButton"
onClick={() => history.push('/signup')}
>
Join
</div>
<div id="menu" className="navBtn">
<img
src={require('../../../public/images/menu.svg')}
onClick={openMenu}
/>
</div>
</div>
)}
</header>
)
}
export default observer(Header)
EDIT
The problem may stem from using MobX for state management and not wrapping the component in a Provider but still unsure of how to do this.

Related

Warning: Functions are not valid as a React child. or how to pass the id into element in Route tag

Actually, I am trying to render the DishDetail component, there is an error like "Warning: Functions are not valid as a React child." Therefore, I cannot render the DishDetail Component under "/menu/:dishId".
Anyone can help me? I have tried googling it. The "DishDetailComponent" and "MainComponent" are below respectively. Thanks a lot!
The MainComponent:
import React, { Component, useContext} from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate, useParams} from 'react-router-dom';
import {connect} from 'react-redux';
const mapStateToProps = (state) => {
return {
dishes: state.dishes,
comments: state.comments,
leaders: state.leaders,
promotions: state.promotions
}}
class Main extends Component{
constructor(props) {
super(props);}
render() {
const HomePage =() => {
return(
<Home
dish={this.props.dishes.filter((dish) => dish.featured)[0]}
promotion={this.props.promotions.filter((promo) => promo.featured)[0]}
leader={this.props.leaders.filter((leader) => leader.featured)[0]}
/>
);
}
const DishWithId = ({match}) => {
return(
<DishDetail dish={this.props.dishes.filter((dish) => dish.id === parseInt(match.params.dishId, 10))[0]}
comments={this.props.comments.filter((comment) => comment.dishId === parseInt(match.params.dishId, 10))}
/>
);
};
return (
<div>
<Header/>
<Routes>
<Route path ="/home" element={HomePage()} />
<Route exact path="/menu" element={<Menu dishes={this.props.dishes} />} />
<Route path='/menu/:dishId' element={<DishWithId />} />
<Route exact path="/contactus" element={<Contact />} />
<Route exact path="/aboutus" element={<About leaders={this.props.leaders}/>} />
<Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>
);}}
export default connect(mapStateToProps)(Main);
The DishDetailComponent:
import React from 'react';
import { Card, CardImg, CardText, CardBody, CardTitle , Breadcrumb, BreadcrumbItem} from 'reactstrap';
import { Link } from 'react-router-dom';
function RenderDish({dish}) {
console.log('Menu Component render is invoked')
return(
<div className="col-12 col-md-5 m-1">
<Card>
<CardImg width="100%" object src={dish.image} alt={dish.name} />
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
</div>
);
}
function RenderComments({comments}) {
const comment = comments.map((comment)=> {
return (
<div key={comment.id} className="col-12 col-md-5 m-1">
<p>{comment.comment}</p>
<p>--{comment.author} , {new Intl.DateTimeFormat('en-US', {year: 'numeric', month: 'long', day: '2-digit' }).format(new Date(comment.date))}</p>
</div>
)
});
return(
<div className="col-12 col-md-5 m-1">
<h4>Comment</h4>
{comment}
</div>
);
}
const DishDetail = (props) => {
if (props.dish != null) {
return (
<div className='container'>
<div className='row'>
<Breadcrumb>
<BreadcrumbItem><Link to="/menu">Menu</Link></BreadcrumbItem>
<BreadcrumbItem active>{props.dish.name}</BreadcrumbItem>
</Breadcrumb>
<div className='col-12'>
<h3>{props.dish.name}</h3>
<hr />
</div>
</div>
<div className='row'>
<div className="col-12 col-md-5 m-1">
<RenderDish dish={props.dish} />
</div>
<div className="col-12 col-md-5 m-1">
<RenderComments comments={props.comments} />
</div>
</div>
</div>
);
}
else {
return (
<div></div>
)
}
} export default DishDetail;
HomePage and DishWithId are function components, so this is simply undefined in both cases. Also, you should completely avoid declaring React components within other React components. They should be declared outside on their own. The filtering should occur in the parent component and the filtered result should be passed as a prop to the routed component.
Example:
import React, { Component, useContext } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate, useParams } from 'react-router-dom';
import { connect } from 'react-redux';
const DishWithId = ({ dishes, comments }) => {
const { dishId } = useParams();
return (
<DishDetail
dish={dishes.find((dish) => String(dish.id) === dishId)}
comments={comments.find((comment) => String(comment.dishId) === dishId)}
/>
);
};
class Main extends Component{
constructor(props) {
super(props);
}
render() {
return (
<div>
<Header/>
<Routes>
<Route
path="/"
element={(
<Home
dish={this.props.dishes.find((dish) => dish.featured)}
promotion={this.props.promotions.find((promo) => promo.featured)}
leader={this.props.leaders.find((leader) => leader.featured)}
/>
)}
/>
<Route
path="/menu"
element={<Menu dishes={this.props.dishes} />}
/>
<Route
path='/menu/:dishId'
element={(
<DishWithId
dishes={this.props.dishes}
comments={this.props.comments}
/>
)}
/>
<Route path="/contactus" element={<Contact />} />
<Route path="/aboutus" element={<About leaders={this.props.leaders}/>} />
<Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>
);
}
}
const mapStateToProps = (state) => ({
dishes: state.dishes,
comments: state.comments,
leaders: state.leaders,
promotions: state.promotions
});
export default connect(mapStateToProps)(Main);
It would be better to ditch the connect HOC and use the useSelector hook directly in the components that need to access the state. This avoid the odd props drilling issue.
Example:
Home
import React from 'react';
import { useSelector } from 'react-redux';
const Home = () => {
const dishes = useSelector(state => state.dishes);
const leaders = useSelector(state => state.leaders);
const promotions = useSelector(state => state.promotions);
const dish = dishes.find((dish) => dish.featured);
const promotion = promotions.find((promo) => promo.featured);
const leader = leaders.find((leader) => leader.featured);
return .....;
};
DishDetail
import React from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
const DishDetail = () => {
const { dishId } = useParams();
const dishes = useSelector(state => state.dishes);
const comments = useSelector(state => state.comments);
const dish = dishes.find((dish) => String(dish.id) === dishId);
const comments = comments.find((comment) => String(comment.dishId) === dishId);
return <DishDetail {...{ comments, dish }} />;
};
... etc for other routed components ...
App
import React, { Component, useContext } from 'react';
import Home from './HomeComponent';
import Menu from './MenuComponent';
import Contact from "./ContactComponent";
import DishDetail from './DishdetailComponent';
import Header from './HeaderComponent';
import About from './AboutComponent';
import Footer from './FooterComponent';
import { Routes, Route, Navigate } from 'react-router-dom';
class Main extends Component{
constructor(props) {
super(props);
}
render() {
return (
<div>
<Header/>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/menu" element={<Menu />}/>
<Route path='/menu/:dishId' element={<DishWithId />}/>
<Route path="/contactus" element={<Contact />} />
<Route path="/aboutus" element={<About />} />
<Route path="*" element={<Navigate replace to="/home" />} />
</Routes>
<Footer />
</div>
);
}
}
export default Main;

`No routes matched location "/" ` warning shown on the console

I am trying to implement a small project and got an error/warning like above title.
Here is my Index.jsx:
import React, { Suspense } from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { BrowserRouter} from "react-router-dom";
import { App } from "./components/app";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<Suspense fallback={<div>Loading...</div>}>
<BrowserRouter>
<App />
</BrowserRouter>
</Suspense>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
App.jsx:
import React, { useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import { Container } from "#material-ui/core";
import { ToastContainer } from "react-toastify";
import { Route, Routes } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import Sidebar from "../sidebar/Sidebar";
import TopNav from "../topnav/TopNav";
import { AppRoutes } from "../AppRoutes";
import ThemeAction from "../../store/actions/ThemeAction";
import "./App.scss";
const useStyles = makeStyles({
contentStyle: {
margin: "30px auto",
},
});
export const App = () => {
const themeReducer = useSelector((state) => state.ThemeReducer);
const classes = useStyles();
const dispatch = useDispatch();
useEffect(() => {
const themeClass = localStorage.getItem("themeMode", "theme-mode-light");
const colorClass = localStorage.getItem("colorMode", "theme-mode-light");
dispatch(ThemeAction.setMode(themeClass));
dispatch(ThemeAction.setColor(colorClass));
}, [dispatch]);
return (
<Routes>
<Route
render={(routeProps) => (
<div className={`app ${themeReducer.mode} ${themeReducer.color}`}>
<Sidebar routeProps={routeProps} />
<div className="app__content">
<TopNav />
<div className="app__content-main">
<ToastContainer />
<Container className={classes.contentStyle} maxWidth="sm">
<AppRoutes />
</Container>
</div>
</div>
</div>
)}
/>
</Routes>
);
};
And AppRoutes.jsx:
import React from "react";
import { Route, Routes } from "react-router-dom";
import Customers from "../pages/Customers";
import { Dashboard } from "../pages/Dashboard";
import { UserLogin } from "./User/Login";
import { UserSignup } from "./User/Signup/UserSignup";
export const AppRoutes = () => {
return (
<Routes>
<Route index path="/" element={<Dashboard />} />
<Route path="customers" component={<Customers />} />
<Route path="userLogin" element={<UserLogin />} />
<Route path="userSignup" element={<UserSignup />} />
</Routes>
);
};
And the project is not running) I mean white blank on browser window while there is no error except index.tsx:25 No routes matched location "/" .
In react-router-dom#6 there are no longer any render (or component or children function props). Remove the Routes and Route component in App. You will also update Sidebar to access the location object via hook.
App
return (
<div className={`app ${themeReducer.mode} ${themeReducer.color}`}>
<Sidebar routeProps={routeProps} />
<div className="app__content">
<TopNav />
<div className="app__content-main">
<ToastContainer />
<Container className={classes.contentStyle} maxWidth="sm">
<AppRoutes />
</Container>
</div>
</div>
</div>
);
Sidebar
const Sidebar = () => {
const location = useLocation();
const activeItem = sidebar_items.findIndex(
(item) => item.route === location.pathname
);
return (
<div className="sidebar">
<div className="sidebar__logo">
<img src={logo} alt="company logo" />
</div>
{sidebar_items.map((item, index) => (
<Link to={item.route} key={index}>
<SidebarItem
title={item.display_name}
icon={item.icon}
active={index === activeItem}
/>
</Link>
))}
</div>
);
};
AppRoutes
Remove the index prop from the "/" route. When you specify an index route the path prop is ignored. Also, make sure all the routes are correctly using the element prop.
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="customers" element={<Customers />} />
<Route path="userLogin" element={<UserLogin />} />
<Route path="userSignup" element={<UserSignup />} />
</Routes>

How to solve this problem ? PrivateRoute with React Router v6

PrivateRoute.js
import React from 'react';
import { Route, Navigate } from 'react-router';
const PrivateRoute = ({component:Component, ...rest}) => {
return <Route {...rest} component={(props) =>{
const token = window.localStorage.getItem('token');
if(token){
return <Component {...props} />
}else{
return <Navigate to ={'/signin'}/>
}
}} />
}
export default PrivateRoute;
App.js
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./main";
import { BrowserRouter as Router } from "react-router-dom";
createRoot(document.getElementById("app")).render(
<Router>
<App />
</Router>,
);
export default App;
Main.jsx
import React from "react";
import { Routes, Route } from "react-router-dom";
import Test from "./Test";
function App() {
return (
<div>
<Routes>
<Route path='/' element={<Test />} />
</Routes>
</div>
);
}
ERROR
Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
Error: A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
I got some error like this. But I use new update of react router v6. Please help me to solve this problem.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Shopping-Cart</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="app.jsx"></script>
</body>
</html>
app.js
import React from "react";
import { render } from "react-dom";
import { Routes, Route, BrowserRouter as Router } from "react-router-dom";
import InsertUser from "./Containers/User/InsertUser";
import ViewUser from "./Containers/User/ViewUser";
import ViewUsers from "./Containers/User/ViewUsers";
render(
<Router>
<Routes>
<Route path="/" element={<InsertUser />} />
<Route path="/viewUsers" element={<ViewUsers />} />
<Route path="/viewUser/:id" element={<ViewUser />} />
</Routes>
</Router>,
document.getElementById("app")
);
insertUser.jsx
import React, { useState } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
function InsertUser() {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const insertData = () => {
const user = {
firstName: firstName,
lastName: lastName,
};
axios
.post("http://localhost:3000/api/user/add", user)
//.then((event) => (window.location.href = "/view-inventory"))
.catch((error) => {
console.error("There was an error!", error);
});
};
return (
<div>
<label for="fname">First name:</label>
<input
type="text"
onChange={(e) => {
setFirstName(e.target.value);
}}
id="fname"
name="fname"
/>
<br />
<label for="lname">Last name:</label>
<input
type="text"
onChange={(e) => {
setLastName(e.target.value);
}}
id="lname"
name="lname"
/>
<br />
<button
onClick={() => {
insertData();
}}
value="Submit"
>
insert
</button>
<br></br>
<button
onClick={() => {
window.location.href = "/viewUsers";
}}
>
View All Users
</button>
</div>
);
}
export default InsertUser;
viweUsers.jsx
import React, { useEffect, useState } from "react";
import axios from "axios";
function ViewUsers() {
const [tableData, setTableData] = useState("");
useEffect(() => {
axios
.get("http://localhost:3000/api/user/getUsers")
.then((response) => {
setTableData(response.data);
})
.catch((error) => {
console.error("There was an error!", error);
});
}, []);
const deteleData = (id) => {
axios
.delete("http://localhost:3000/api/user/delete/" + id)
.then(alert("Deleted"));
};
return (
<div>
<table>
<thead>
<tr>
<th>FirstName</th>
<th>LastName</th>
</tr>
</thead>
<tbody>
{tableData.length > 0
? tableData.map((data) => (
<tr
key={data._id}
onClick={() => {
window.location.href = `/viewUser/${data._id}`;
}}
>
<td>{data.firstName}</td>
<td>{data.lastName}</td>
<td>
<button
onClick={() => {
deteleData(data._id);
}}
>
Delete
</button>
</td>
</tr>
))
: null}
</tbody>
</table>
<br></br>
</div>
);
}
export default ViewUsers;
Route components are only valid as children of a Routes or other Route components, and as the error message says, you can't render it directly in an other component alone. In react-router-dom v6 custom Route components are no more, instead the suggested pattern is to use wrapper components to hold the auth logic and conditionally return the content or a Navigate redirect.
Convert PrivateRoute into a PrivateWrapper:
import { Navigate, Outlet } from "react-router-dom";
const PrivateWrapper = () => {
const token = window.localStorage.getItem('token');
return token ? <Outlet /> : <Navigate to='/signin' replace/>;
}
Render the wrapper around Route components you want to protect access to. The Outlet is where the children routes will be rendered
function App() {
return (
<div className="App">
<Router>
<Routes>
<Route path="/" element={<PrivateWrapper/>}>
<Route path="/" element={ <Home />}/>
</Route>
<Route path="/signin" element={ <Signin />} />
<Route path="/signup" element={ <Signup />} />
</Routes>
</Router>
</div>
);
}

How to get id from json with axios get request

I'm trying to get idrestaurant for use as identifier from json document that has info like this on json objects on the database. Stuff is on PostgreSQL
This is a react website for a school project. I have tried printing response.data on a console log but nothing shows.
[{"idrestaurant":2,
"name":"Pizzeria Amigo",
"address":"Uusikatu 16",
"opening":"08:00",
"closing":"12:00",
"img":"testi.com/kuva.jpg",
"type":"fastfood",
"pricelvl":"3",
"owneruserid":1},
{"idrestaurant":3,
"name":"Burgers",
"address":"Katu 10",
"opening":"08:00",
"closing":"18:00",
"img":"testi.com/kuva.png",
"type":"fastfood",
"pricelvl":"1",
"owneruserid":1}]
I want to use id as idrestaurant as unique key for printing list of restaurant elements at Restaurant.js. Here is what I have been trying to do.
App.js
import React, { useEffect, useState } from 'react'
import './App.css'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import NavBar from './components/navBar'
import Restaurants from './components/Restaurants'
import searchRestaurant from './components/Search'
import Restaurant from './components/Restaurant'
import axios from 'axios'
const App = () => {
const [restaurants, setRestaurants] = useState([])
useEffect(() => {
axios.get('http://MadeUpURL-app.herokuapp.com/restaurants')
.then((response) => {
setRestaurants(response.data)
});
}, []);
return (
<Router>
<div>
<NavBar />
<Routes>
<Route path='/restaurants' element={<Restaurants restaurants={restaurants} />} >
<Route path="/restaurants/idrestaurant" element={<Restaurant restaurants={restaurants} />} />
</Route>
{/* <Route path='/Search' element={<Search />} /> */}
</Routes>
</div>
</Router>
);
}
export default App
Restaurants.js
import React from 'react'
import styles from './restaurants.module.css'
import Restaurant from './Restaurant'
import { Link } from 'react-router-dom'
const Restaurants = (props) => {
return (
<div className={styles.container}>
<div className={styles.restaurantList}>
{props.restaurants.map(restaurant => <Link to={restaurant.idrestaurant}>
<Restaurant key={restaurant.idrestaurant} />
</Link>
)}
</div>
</div>
)
}
export default Restaurants
Restaurant.js
import React from 'react'
import styles from './restaurant.module.css'
export default function Restaurant(props) {
return (
<div className={styles.shop}>
<div>
<div><img src={props.img} className={styles.imageSize} /></div>
<div>
<div className={styles.title}>{props.name}</div>
<div className={styles.type}>{props.type}</div>
<div className={styles.prange}>{props.pricelvl}</div>
</div>
</div>
</div>
)
}
it is preferable to call api inside the Restaurants Component. Because setRestaurants will take time to update the state and in the mean time Restaurants Component will be render already so restaurants will be null in <Restaurants restaurants={restaurants} />
WORKING DEMO
Here is the code for App.js
import React, { useEffect, useState } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Restaurants from "./components/Restaurants";
import Restaurant from "./components/Restaurant";
const App = () => {
return (
<Router>
<div>
<Routes>
<Route path="/" element={<Restaurants />} />
<Route path="/restaurants" element={<Restaurants />}>
<Route path="/restaurants/idrestaurant" element={<Restaurant />} />
</Route>
{/* <Route path='/Search' element={<Search />} /> */}
</Routes>
</div>
</Router>
);
};
export default App;
Here is the code for Restaurants.js
import React, { useEffect, useState } from "react";
import Restaurant from "./Restaurant";
import { Link } from "react-router-dom";
import axios from "axios";
const Restaurants = (props) => {
const [restaurants, setRestaurants] = useState([]);
const jsonFakeData = [
{
idrestaurant: 2,
name: "Pizzeria Amigo",
address: "Uusikatu 16",
opening: "08:00",
closing: "12:00",
img: "testi.com/kuva.jpg",
type: "fastfood",
pricelvl: "3",
owneruserid: 1
},
{
idrestaurant: 3,
name: "Burgers",
address: "Katu 10",
opening: "08:00",
closing: "18:00",
img: "testi.com/kuva.png",
type: "fastfood",
pricelvl: "1",
owneruserid: 1
}
];
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/users").then((response) => {
console.log(response.data);
//setRestaurants(response.data);
setRestaurants(jsonFakeData);
});
}, []);
return (
<div>
<div>
{restaurants && restaurants.length > 0
? restaurants.map((restaurant) => (
<Link key={restaurant.idrestaurant} to={restaurant.idrestaurant}>
<Restaurant
restaurant={restaurant}
key={restaurant.idrestaurant}
/>
</Link>
))
: ""}
</div>
</div>
);
};
export default Restaurants;
Here is the code for Restaurant.js
import React from "react";
export default function Restaurant(props) {
console.log(props, "prps");
return (
<div>
<div>
<div>
<img src={props.restaurant.img} alt="img" />
</div>
<div>
<div>{props.restaurant.name}</div>
<div>{props.restaurant.type}</div>
<div>{props.restaurant.pricelvl}</div>
</div>
</div>
</div>
);
}

react: React-router Link not loading a component

I have a login page which has a forgot password link and it takes the user to forgot password page.
When I click on the forgot password link, it changes the URL but does not load the component.
Code for login page
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
// assets
import Logo from "../../../assets/images/kvh-logo.svg";
import bgImgArray from "../../../assets/images/bg";
import { Button, Form, Input, InputGroup, InputGroupAddon } from "reactstrap";
import "./Login.css";
const Login = (props) => {
const [loading, setLoading] = useState(false);
const userid = useFormInput("");
const password = useFormInput("");
const [error, setError] = useState(null);
// for changing backgrounds
const [index, setIndex] = useState(0);
return (
<div className="container-fluid backgroundContainer">
<div className="Login">
<div className="login-form-container">
<div className="logo">
<img src={Logo} className="App-logo" alt="logo" />
</div>
<div className="content">
<Form className="login-form">
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-user"></i>
</InputGroupAddon>
<Input
autoFocus
type="email"
aria-label="Username"
aria-describedby="username"
aria-invalid="false"
placeholder="Username or Email"
{...userid}
/>
</InputGroup>
<InputGroup>
<InputGroupAddon
className="input-group-addon"
addonType="prepend"
>
<i className="fa fa-lock"></i>
</InputGroupAddon>
<Input
type="password"
placeholder="Password"
aria-label="password"
aria-describedby="password"
{...password}
/>
</InputGroup>
<div className="form-actions">
{error && (
<>
<small style={{ color: "red" }}>{error}</small>
<br />
</>
)}
<br />
<Button
className="pull-right"
block="true"
type="submit"
bssize="small"
value={loading ? "Loading..." : "Login"}
onClick={handleLogin}
disabled={loading}
>
Login
</Button>
<br />
</div>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
</Form>
</div>
</div>
</div>
</div>
);
};
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default Login;
In routing code, I have Admin Layout which looks after the dashboard and AuthLayout which looks after the Login page.
I tried searching for the solution but unfortunately couldn't find any solutions. Hence, posting it here.
Router Code
import React from "react";
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
import { createBrowserHistory } from "history";
import AdminLayout from "layouts/Admin/Admin.js";
import AuthLayout from "layouts/Auth/Auth.js";
import ResetPassword from "../components/pages/reset-password/ResetPassword";
const hist = createBrowserHistory();
const AppRouter = () => {
return (
<Router history={hist}>
<Switch>
<Route path="/admin" render={(props) => <AdminLayout {...props} />} />
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
<Redirect from="/" to="/auth" />
</Switch>
</Router>
);
};
export default AppRouter;
Adding Auth Layout Code
import React from "react";
import { Route, Switch, Redirect, Link } from "react-router-dom";
import Login from "../../components/pages/login/Login";
import ResetPassword from "../../components/pages/reset-password/ResetPassword";
import routes from "routes/routes.js";
class Pages extends React.Component {
getRoutes = (routes) => {
return routes.map((prop, key) => {
if (prop.collapse) {
return this.getRoutes(prop.views);
}
if (prop.layout === "/auth") {
return (
<Route
path={prop.layout + prop.path}
component={prop.component}
key={key}
/>
);
} else {
return null;
}
});
};
getActiveRoute = (routes) => {
let activeRoute = "WATCH";
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = this.getActiveRoute(routes[i].views);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else {
if (
window.location.pathname.indexOf(
routes[i].layout + routes[i].path
) !== -1
) {
return routes[i].name;
}
}
}
return activeRoute;
};
componentDidMount() {
document.documentElement.classList.remove("nav-open");
}
render() {
return (
<div className="wrapper wrapper-full-page" ref="fullPages">
<div className="full-page">
<Login {...this.props}></Login>
<div className="forgotPassword">
<Link to="/forgotPassword">Forgot password?</Link>
</div>
<Switch>
{this.getRoutes(routes)}
<Redirect from="*" to="/auth/login" />
</Switch>
</div>
</div>
);
}
}
export default Pages;
look at this code that you wrote:
<Route path="/" render={(props) => <AuthLayout {...props} />} />
<Route path="/forgotPassword" component={ResetPassword} />
it's never going to /forgotPassword because path always match with first Route.
you should use exact props:
<Route exact path="/" render={(props) => <AuthLayout {...props} />} />
<Route exact path="/forgotPassword" component={ResetPassword} />
If you create your own history then you should use Router instead of BrowserRouter.
import {
Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
Because, BrowserRouter comes with its own history and you provide another one, this causes the problem.
Recently I faced this issue with React 18.
Version of react was 18.0.1 and react-router-dom was 5.1.0 so StrictMode is causing the issue
Just remove or comment the Tag <React.StrictMode> and it should work fine.
Wrap inside Router as given below.
import {
BrowserRouter as Router,
} from "react-router-dom";
<Router><Link to="/forgotPassword">Forgot password?</Link> <Router/>

Resources