cannot render a <Router> inside another <Router> - reactjs

I'm trying to render out the dropdown menu items from DropDown.js whenever i click on Services button from Navbar.js. However, when i click on Services button i got this error "You cannot render a inside another . You should never have more than one in your app.". How can i fix this? Please help me! Thank you so much!
DropDown.js:
import React from "react";
import { serviceDropdown } from "./NavItems";
import { Link, BrowserRouter as Router } from "react-router-dom";
import "./DropDown.css";
function DropDown() {
return (
<div>
<ul className="subnav-items">
{serviceDropdown.map((item) => {
return (
<Router>
<li key={item.id} className={item.cName}>
<Link to={item.path}>{item.title}</Link>
</li>
</Router>
);
})}
</ul>
</div>
);
}
export default DropDown;
Navbar.js:
import React, { useState } from "react";
import { navItems } from "./NavItems";
import { Link, BrowserRouter as Router } from "react-router-dom";
import "./Navbar.css";
import DropDown from "./DropDown";
function Navbar() {
const [dropDown, setDropdown] = useState(false);
return (
<div>
<ul className="nav-items">
{navItems.map((item) => {
if (item.title === "Services") {
return (
<Router>
<li
key={item.id}
className={item.cName}
onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}
>
<Link to={item.path}>{item.title}</Link>
{dropDown && <DropDown />}
</li>
</Router>
);
}
return (
<Router>
<li key={item.id} className={item.cName}>
<Link to={item.path}>{item.title}</Link>
</li>
</Router>
);
})}
</ul>
</div>
);
}
export default Navbar;

It's like the invariant error says, you should only have one router providing a routing context to the entire app, or at a minimum, not have any nested routers. Remove all the extraneous Router components wrapping each individual list item/Link, and ensure you've only one wrapping the app.
DropDown.js:
import React from "react";
import { serviceDropdown } from "./NavItems";
import { Link } from "react-router-dom";
import "./DropDown.css";
function DropDown() {
return (
<div>
<ul className="subnav-items">
{serviceDropdown.map((item) => {
return (
<li key={item.id} className={item.cName}>
<Link to={item.path}>{item.title}</Link>
</li>
);
})}
</ul>
</div>
);
}
export default DropDown;
Navbar.js:
import React, { useState } from "react";
import { navItems } from "./NavItems";
import { Link } from "react-router-dom";
import "./Navbar.css";
import DropDown from "./DropDown";
function Navbar() {
const [dropDown, setDropdown] = useState(false);
return (
<div>
<ul className="nav-items">
{navItems.map((item) => {
if (item.title === "Services") {
return (
<li
key={item.id}
className={item.cName}
onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}
>
<Link to={item.path}>{item.title}</Link>
{dropDown && <DropDown />}
</li>
);
}
return (
<li key={item.id} className={item.cName}>
<Link to={item.path}>{item.title}</Link>
</li>
);
})}
</ul>
</div>
);
}
export default Navbar;
App example:
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
...
return (
<Router>
<DropDown />
<Navbar />
<Routes>
.... routes here ....
</Routes>
</Router>
);

Related

Getting Invalid Hook Error in React App after trying to wrap app in provider

I am building a mock e-commerce store using react and after laying the groundwork for the website, I installed the package react-use-cart (https://www.npmjs.com/package/react-use-cart) to handle the items being added to the cart and to be used in a checkout page. I wrapped my app in the Cart Provider that was given by the react-use-cart package and set up the cart functions on my product pages but now my app does not load anything on startup and gives me an invalid hook call error. I have tried wrapping the app in the index.js file with the provider as well and that gives the same error.
import React, { useState } from "react";
import './css/App.css';
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
import NavBar from "./components/NavBar"
import ProductsScreen from "./screens/ProductsScreen"
import ProductDetailScreen from "./screens/ProductDetailScreen"
import HomeScreen from "./screens/HomeScreen"
import LoginScreen from "./screens/LoginScreen"
import RegisterScreen from "./screens/RegisterScreen";
import CartScreen from "./screens/CartScreen"
import { CartProvider } from "react-use-cart";
const App = () => {
return (
<>
<BrowserRouter>
<CartProvider>
<NavBar />
<Routes>
<Route path='/' element={<HomeScreen/>}/>
<Route path='/products' element={<ProductsScreen/>}/>
<Route path='/product/:id' element={<ProductDetailScreen/>}/>
<Route path='/login' element={<LoginScreen/>}/>
<Route path='/register' element={<RegisterScreen/>}/>
<Route path='/cart' element={<CartScreen/>}/>
</Routes>
</CartProvider>
</BrowserRouter>
</>
)
}
export default App;
import { useState, useEffect }from 'react'
import { Link, useParams } from 'react-router-dom'
import axios from 'axios'
import "../css/ProductDetailScreen.css"
import { useCart } from "react-use-cart";
// import Slideshow from '../components/SlideShow'
const { addItem, updateItemQuantity } = useCart();
const ProductDetailScreen = ( {match} ) => {
const [product, setProduct] = useState({})
const { id } = useParams()
useEffect(() => {
const getProduct = async () => {
const { data } = await axios.get(`/api/products/${id}`)
setProduct(data)
}
getProduct()
}, [])
return (
<>
<Link to ='/products'>Go Back</Link>
<div className="wrapper">
<div className="detail-grid">
<div className='imgContainer'>
<img className='cover_img1' src={product.cover_img} alt='Videogame Cover Art'/>
<div className='carousel'>
{product.images?.map((image, index) => (
<img key={index} src={image} alt="Videogame Gameplay"></img>
))}
</div>
{/*Replace carousel div with actual image slideshow*/}
</div>
<div>
<div className='textContainer'>
<h1 className='title1'>{product.title}</h1>
<h3 className='descriptionHeader'>Description:</h3>
<p className='description1'>{product.description}</p>
<h3 className='developer1'>Developer: {product.developer}</h3>
<h3 className='publisher1'>Publisher: {product.publisher}</h3>
<h3 className='releaseDate1'>Release Date: {product.releaseDate}</h3>
<h3 className='genre1'>Genre: {product.genre}</h3>
</div>
<div className='checkOutContainer'>
<h3 className='price1'>Price: ${product.price}</h3>
<button onClick={() => updateItemQuantity(product.id, product.quantity - 1)}> - </button>
<span>{product.quantity}</span>
<button onClick={() => updateItemQuantity(product.id, product.quantity + 1)}> + </button>
<button onClick={() => addItem(product)} className='addCart' type='button'>Add to Cart</button>
</div>
</div>
</div>
</div>
</>
)
}
export default ProductDetailScreen

How do I use the Use Navigate from React Router on a button that links to different pages?

I have a button that represents shop now that upon click it redirects you on different pages and the card elements have been mapped on external data.
I want this button to navigate to different pages kindly help.
const data =[
{
id:"Extensions",
Title:"Electrical Collections",
img:"/Assets/Electricals/Extlogo.png",
},
{
id:"Phone Accesorries",
Title:"Phone Accessories",
img:"/Assets/Phone/phoneacc.png",
},
{
id:"Homeware",
Title:"Homeware Collections",
img:"/Assets/Home/home.png",
},
]
function Home() {
let Navigate =useNavigate();
const handleElectricalPage = () =>{
Navigate("/extensionproduct")
}
<div className='cardContainer'>
{data.map((item )=>(
<card>
<div className='imageContainer'>
<img src={item.img} alt='Extension logo'/>
</div>
<div className='contentsContainer'>
<h2>{item.Title}</h2>
<div className='iconButtonContainer'>
<button onClick={handleElectricalPage}>Shop Now</button>
<ArrowForwardIcon className='arrowIcon'/>
</div>
</div>
Example from the react router https://reactrouter.com/docs/en/v6/api#usenavigate
navigate("../success", { replace: true });
You need to make an onClick handler (handleElectricalPage) dynamically, consider something like this.
function Home() {
let navigate =useNavigate();
return (
<div className='cardContainer'>
{data.map((item) => (
<card>
<button
onClick={() => navigate(`/externalProduct/${item.id}`)}
>
Shop Now
</button>
</card>
)}
</div>
}
You can also use Link which handles everything by itself
<Link to={`/externalProduct/${item.id}`}>
<button>Shop now</button>
</Link>
App.jsx
import { Routes, Route, Navigate } from "react-router-dom";
function App() {
return (
<div className="App">
<Routes>
<Route path="/" element={<Home />}>
<Route path="post/:id" element={<Detail />} />
</Route>
</Routes>
</div>
);
}
export default App;
Post.jsx
import React, { useState } from "react";
import {useNavigate} from 'react-router-dom'
export default function Post(props) {
const navigate = useNavigate()
{props.posts.map((post) => {
return (
<div className="post__container" key={post.id}>
<h4>
{post.id} {post.title}
</h4>
<p>{post.body}</p>
<button onClick={() => {return navigate(`${post.id}`)}}>Detail</button>
</div>
);
})}
}
Detail.jsx
import axios from 'axios'
import React, {useEffect, useState } from 'react'
import {useParams} from 'react-router'
export default function Detail (props) {
const params = useParams()
const [posts, setPosts] = useState({})
async function getById(id) {
const response = await axios.get("https://jsonplaceholder.typicode.com/posts/" + id)
setPosts(response.data)
}
useEffect(() => {
getById(params.id)
}, [params.id])
return (
<div>
<h2>Detail page: {params.id}</h2>
<h4>{posts.title}</h4>
<h5>{posts.body}</h5>
</div>
)
}

How to close sidenav when I click anywhere of the page in react js

**Nav-header.js**
The sidebar should not collapse when I am clicking outside of the page in react js. Please help with that. this is the code I wrote for the side menu. I am a beginner at the react js. Please help
import React, { useState } from 'react'
import * as FaIcons from 'react-icons/fa'
import * as AiIcons from 'react-icons/ai'
import { Link } from 'react-router-dom'
import { SidebarData } from './SidebarData'
import './Navbar.css'
import { IconContext } from 'react-icons'
const Header = () => {
const [sidebar, setsidebar] = useState(false)
const showSideBar = () => setsidebar(!sidebar)
return (
<>
<IconContext.Provider value={{ color: '#1D1D1D' }}>
<div className='navbar'>
<Link to="#" className='menu-bars'>
<FaIcons.FaBars onClick={showSideBar} color="#009540"/>
</Link>
<span className="meny">Meny</span>
</div>
<nav className={sidebar ? 'nav-menu active' : 'nav-menu' } >
<ul className='nav-menu-items' onClick={showSideBar}>
<li className='navbar-toggle'>
<Link to="#" className='menu-bars-logo'>
<AiIcons.AiOutlineClose/>
</Link>
</li>
{SidebarData.map((item, index) => {
return (
<li key={index} className={item.cName}>
<Link to={item.path}>
<span>{item.title}</span>
</Link>
</li>
)
})}
</ul>
</nav>
</IconContext.Provider>
</>
)
}
export default Header
You can use onblur, in react it would look like
return (
<div onBlur={() => {
... do something like setState
}}>
)

how to re-render all related components with i18n strings once language is changed

this main App function has a custom hook that will trigger when the button is cliked:
import React, { useEffect } from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom'
import HomeScreen from './Screens/HomeScreen'
import './App.css';
import { useCurrentLang } from './utils/useCurrentLang'
import {strings as engstrings} from './res/lang/eng/strings'
function App() {
const currentStrings = useCurrentLang(engstrings);
return (
<BrowserRouter>
<div className="grid-container">
<header className="header">
<div className="brand">
<Link to="/" >
</Link>
</div>
<div className="header-side">
{currentStrings.currentlang.subtitle}
</div>
<div className="header-right">
<button {...currentStrings}>
{currentStrings.currentlang.traduction}
</button>
</div>
<div>
</div>
</header>
<main className="main">
<div className="content">
<Route path="/" exact={true} component={HomeScreen} />
</div>
</main>
<footer className="footer">
© 2020
</footer>
</div>
</BrowserRouter>
);
}
export default App;
However this component function that is routed from App also needs to use the same reference of the hook object found in App:
import React from 'react';
import terminalImage from '../res/images/GNOMETerminalIcon.png';
import {useCurrentLang} from '../utils/useCurrentLang'
import {strings as engstrings} from '../res/lang/eng/strings'
const { Link } = require("react-router-dom");
function HomeScreen() {
const currentStrings = useCurrentLang(engstrings);
return <div className="home">
<ul className="menu-list">
<li>
<div className="about-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
<li>
<div className="projects-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
<li>
<div className="contacts-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
<li>
<div className="suggestions-link section">
<Link to="/about">{currentStrings.currentlang.about}</Link>
</div>
</li>
</ul>
<div className="home-main-image">
<img src={terminalImage} />
</div>
</div>
}
export default HomeScreen;
Is it possible for both function to rerender when the hook on App is triggered? if yes, how?
Edit: currentLang hook:
import {useState} from 'react'
import {strings as frstrings} from '../res/lang/fr/strings'
import {strings as engstrings} from '../res/lang/eng/strings'
export const useCurrentLang = initialState => {
if(initialState === 0){
initialState = engstrings
}
const [currentlang, setLang] = useState(initialState);
return {
currentlang: currentlang,
onClick: () => {
if(currentlang === engstrings){
setLang(frstrings)
} else {
setLang(engstrings)
}
}
}
}
Problem
The main problem you're facing is one part of your React Tree has no idea that the language has changed.
Solution
Use a Language Context which provides the updates to all your React tree and wrap it on the top of the application. In layman terms, now your app is on listening mode whenever lang changes. So basically, what React does now is whenever lang changes, it will find wherever lang is used from the context and update the component.
Docs on React context here
import React from 'react'
// import {strings as frstrings} from '../res/lang/fr/strings'
// import {strings as engstrings} from '../res/lang/eng/strings'
const lang = {
// in this way, you could dynamically add lang
// later on which worrying about if-elses in your component
en: {
hello: 'hello'
},
fr: {
hello: 'bonjour',
},
}
const langDict = (key) => lang[key]
const LanguageContext = React.createContext(null);
function LanguageProvider({ initialState = 'en', children }) {
const [lang, setLang] = React.useState(initialState);
return (
<LanguageContext.Provider value={[langDict(lang), setLang]}>
{children}
</LanguageContext.Provider>
)
}
function useLanguage() {
return React.useContext(LanguageContext);
}
export default function AppWrapper() {
return (
<LanguageProvider>
<App />
</LanguageProvider>
)
}
function App() {
const [lang, setLang] = useLanguage();
return (
<div>
<h1>{lang.hello}</h1>
<button onClick={() => setLang('fr')}>French</button>
<button onClick={() => setLang('en')}>English</button>
</div>
)
}

React function will not rerender even if state hook called

In this function:
import React, { useEffect } from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom'
import HomeScreen from './Screens/HomeScreen'
import './App.css';
import currentStrings from './utils/currentlang'
function App() {
const [currentlang, setCurrentStrings] = React.useState(currentStrings)
return (
<BrowserRouter>
<div className="grid-container">
<header className="header">
<div className="brand">
<Link to="/" >
</Link>
</div>
<div className="header-side">
{currentlang.getCurrent.subtitle}
</div>
<div className="header-right">
<button onClick={() => setCurrentStrings(currentlang.switchLang())}> {currentlang.getCurrent.traduction} </button>
</div>
<div>
</div>
</header>
<main className="main">
<div className="content">
<Route path="/" exact={true} component={HomeScreen} />
</div>
</main>
<footer className="footer">
© 2020
</footer>
</div>
</BrowserRouter>
);
}
export default App;
I use a button (<button onClick={() => setCurrentStrings(currentlang.switchLang())}> ) to change one of the properties of the object currentStrings (or currentLang used for the useState hook)
However it seems my function does not rerender, i can confirm this since my logs do tell the object does change its properties when the button is clicked, but the screen does not show it.
import {strings as frstrings} from '../res/lang/fr/strings'
import {strings as engstrings} from '../res/lang/eng/strings'
class CurentLang {
constructor(){
this.current = engstrings;
}
switchLang() {
if(this.current === frstrings){
this.current = engstrings;
console.log("engstrings");
} else{
this.current = frstrings;
console.log("frstrings");
}
return new this.CurentLang;
}
get getCurrent(){
return this.current;
}
}
var currentStrings = new CurentLang()
export default currentStrings;

Resources