React nested routes not loading components - reactjs

for some reason this simple setup doesnt seem to work, what am i forgetting ?
import React from "react";
import { Route, Link } from "react-router-dom";
const Account = () => {
return (
<div>
Account
go back to <Link to="/menu">menu</Link>
</div>
)
}
const Book = () => {
return (
<div>
Book
go back to <Link to="/menu">menu</Link>
</div>)
}
const Menu = props => {
return (
<div>
<Route path="/menu" render={() => {
return (
<div>
home
<br /><Link to="menu/account">account</Link>
<br /><Link to="menu/book">book</Link>
</div>
)
}} />
<Route exact path="/menu/account" component={Account} />
<Route exact path="/menu/book" component={Book} />
</div>
)
}
export default Menu;
theres a second issue on top of this one, in my bigger project the nested routes work but only when the route is called from the "Link tag" element, and breaks when called from refresh or adress bar (CSS goes crazy)
thank you.

Related

React JS Login and Router are not working as expected

I am trying to create login functionality in Reactjs, where I want user to be redirected to health page if he/she haven't signed in.
Login.jsx
import './Login.css';
import { PropTypes } from 'prop-types';
import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';
const Login = (props) => {
let history = useHistory();
const [email, setEmail] = useState();
const [pwd, setPwd] = useState();
// state = {
// email: '',
// pwd: '',
// };
const handleSubmit = (e) => {
e.preventDefault();
props.isLogIn(true);
};
Login.propTypes = {
isLogIn: PropTypes.boolean,
};
// function validateForm() {
// return email.length > 0 && pwd.length > 0;
// }
return (
<div className="div-login">
{/* <div className="div-login-logo">
<Logo />
</div> */}
<div>
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
placeholder="email..."
required
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={pwd}
placeholder="password..."
required
onChange={(e) => setPwd(e.target.value)}
/>
<button
onClick={() => {
history.push('/health');
}}
>
Log In
</button>
</form>
</div>
</div>
);
};
export default Login;
Here is App.js where I have written the logic using Redirect, however I am not getting the right results.
import React, { useEffect, useState } from 'react';
import {
BrowserRouter as Router,
Redirect,
Route,
Switch,
} from 'react-router-dom';
import Header from './components/Header/Header';
import Health from './components/Health/Health';
import Login from './components/Login/Login';
import NotFound from './components/NotFound/NotFound';
import SidebarNav from './components/SidebarNav/SidebarNav';
import UserAdmin from './components/UserAdmin/UserAdmin';
const App = () => {
const [hideMenu, setHideMenu] = useState(false);
const [state, setState] = useState();
useEffect(() => {
setHideMenu(window.location.pathname === '/');
}, [window.location.pathname]);
const handleLogin = (isLog) => {
setState({ isLog });
};
let isLog = state;
return (
<React.Fragment>
<Router>
{hideMenu && (
<React.Fragment>
<SidebarNav />
<Header />
</React.Fragment>
)}
<div className={hideMenu ? '' : 'content'}>
<main>
<Switch>
{
<Route exact path="/">
{!isLog ? (
<Redirect to="/login " />
) : (
<Login isLogIn={handleLogin} />
)}
</Route>
}
{/* <Route path="/" exact component={Login} /> */}
{/* <Route path="/login" exact component={Login} /> */}
<Route path="/health" exact component={Health} />
<Route path="/useradmin" exact component={UserAdmin} />
<Route path="*" component={NotFound} />
</Switch>
</main>
</div>
</Router>
</React.Fragment>
);
};
export default App;
Expectation
As the user opens the url firstly he/she should see the login page, if the user is logged in he/she should be redirected to the /health page.
As I am new to Reactjs, Any help or suggestion will be appreciated. Thanks
I agree with #Yosef. Use State variable for boolean comparison instead of storing it in a variable.
I will suggest you should create a separate component for Private Routes. When user is logged in it ternary operator will go into Private Routes component. and if user is not logged in then ternary operator will show login page.
Even if you try to redirect to health page from URL. it will not move into Private Routes component. I did a small Route Protected Example for you.
Take a look at it.
Also you should checkout the documentation of React Router.
here is good article about protected routes
https://ui.dev/react-router-protected-routes-authentication/
Working Example:
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,Redirect
} from "react-router-dom";
export default function BasicExample() {
const [login, setlogin] = React.useState(false)
return (
<>
<div>
<button name="btn" onClick={() => setlogin(true)} > login </button>
</div>
<Router>
<div>
<Switch>
<>
{!login ? (
<Route path="/">
<Redirect to="/login" />
</Route>
) : (
<PrivateRoute />
)}
<Route path="/login" exact component={Login} />
</>
</Switch>
</div>
</Router>
</>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Login() {
return (
<div>
<h2>Login</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
function PrivateRoute ({component: Component, authed, ...rest}) {
const routes = [
{
path: '/',
component: Home,
},
{
path: '/dashboard',
component: Dashboard,
},
{
path: '/about',
component: About,
}]
return (
<>
<ul>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
{routes.map(({ path, component }) => (
<Route exact path={path} component={component} />
))}
<Redirect exact to="/" />
</>
)
}
1. Found this in your code:
const handleLogin = (isLog) => {
setState({ isLog });
};
let isLog = state;
See that you update state to be object with isLog property, while when you use it you expect it to be a primitive boolean.
after setState({isLog}), assuming isLog is false, you got state = {isLog: false}.
then the variavle let isLog = state; is actually not a boolean, but an object, which is a truthy variable whatever properties its have.
so when you check for if(isLog) you will always get true.
Change setState({ isLog }); to setState(isLog);
2. I not get the login of the following check:
{!isLog ? (
<Redirect to="/login " />
) : (
<Login isLogIn={handleLogin} />
)}
What its currently do is:
If user not logged in, redirect from homepage to login page (while you commented out the login route), if he logged in, display login page in homepage route.
What I guess you want to happen is:
If user not logged in, redirect from homepage route to login route, and render login page on login route, if he does logged in, redirect from homepage route to health route, and render health page in health route.
The above suppose to looks like this:
<Switch>
{
<Route exact path="/">
{!isLog ? (
<Redirect to="/login" />
) : (
<Redirect to="/health"/>
)}
</Route>
}
<Route path="/login" exact ><Login isLogIn={handle login}/>
<Route path="/health" exact component={Health} />
<Route path="/useradmin" exact component={UserAdmin} />
<Route path="*" component={NotFound} />
</Switch>

Infinite Loop in Reach Router

I'm a bit new to React and it is my first time using reach-router (or any kind of router really). What I'm trying to do is have a nested component inside one of my router links. Basically, within my ItemShop component, I want to have two more links to components (both of which are defined within my ItemShop component), and I want to display whichever component is selected under the navbar. It seems similar to something they do in the tutorial, but for some reason I seem to get an infinite loop when I click on a link.
Here is my top-level router, in App.js:
function App() {
return (
<div>
<Router>
<HomePage path="/" />
<ItemShop path="ItemShop" />
<Item path="ItemShop/:id" />
<Challenge path="Challenge" />
<Achievements path="Achievements" />
<BattlePass path="BattlePass" />
<Miscellaneous path="Miscellaneous" />
</Router>
</div>
);
}
And this is my ItemShop component where I'm trying to render the links, ItemShop.js:
render() {
// ... assigning arrays here
let Current = () => ( //...);
let Upcoming = () => ( //...);
return(
<>
<div className="nav-container">
<Navbar />
</div>
//...
<div>
<nav className="side-nav">
<Link to="/current">Current</Link>{" "}
<Link to="/upcoming">Upcoming</Link>
</nav>
<Router>
<Current path="current" />
<Upcoming path="upcoming" />
</Router>
</div>
//...
{this.props.children}
)
}
}
Again I am very new to Javascript/React as a whole, so it could just be a fundamental flaw. I have already sunk quite a few hours into this so I would really appreciate some guidance. Thank you for your time!
I tried using React-Router-Dom instead of reach-router. I made it so it renders both <Upcoming /> and <Current /> components inside of the <ItemShop /> component. You can check it out how I have done it below. I hope this helps.
// import React from "react";
// import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
export default function App() {
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/itemShop" component={ItemShop} />
<Route path="/itemShop/:id" component={Item} />
<Route path="/challenge" component={Challenge} />
<Route path="/achievements" component={Achievements} />
<Route path="/battlePass" component={BattlePass} />
<Route path="/miscellaneous" component={Miscellaneous} />
</Switch>
</Router>
</div>
);
}
const HomePage = () => {
return <div>Home Page</div>;
};
const ItemShop = () => {
const Current = () => {
return <div>Current</div>;
};
const Upcoming = () => {
return <div>Upcoming</div>;
};
return (
<div>
<div>Item Shop</div>
<Link to="/itemShop/current">Current</Link>{" "}
<Link to="/itemShop/upcoming">Upcoming</Link>
<br />
<br />
<Route
render={() =>
window.location.pathname === `/itemShop/current` ? (
<Current />
) : (
<Upcoming />
)
}
/>
</div>
);
};
const Item = () => {
return <div>Item</div>;
};
const Challenge = () => {
return <div>Challenge</div>;
};
const Achievements = () => {
return <div>Achievements</div>;
};
const BattlePass = () => {
return <div>BattlePass</div>;
};
const Miscellaneous = () => {
return <div>Miscellaneous</div>;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/6.0.0-beta.0/react-router-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Open modal in React Router

In my react project, this is my App.js:
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Modal from "./Modal";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/modal">
<Modal />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
<p>
Please <Link to="/modal/1">Click Here</Link> for see details.
</p>
</div>
);
}
When you click on "Click Here", the modal was open, but my home page will be disappear. how can open this modal without destroying the home page ?
DEMO HERE:
https://codesandbox.io/s/react-router-basic-2g9t1
Modals should not be in a route as they are supposed to be on top of another page, not a page themshelves. If you want my opinion I would suggest you to put the modal in any of the pages and control if it is opened or not with a react state:
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Modal from "./Modal";
export default function BasicExample() {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Home} />
</Switch>
</div>
</Router>
);
}
const Home = () => {
const [ isModalOpened, setModalOpened ] = useState(false);
return (
<div>
<h2>Home</h2>
<button onClick={() => setModalOpened(!isModalOpened)}
<Modal isOpened={isModalOpened}>
...modal content here
</Modal>
</div>
);
}
And your modal component should look like something like this:
const Modal = ({ isOpened, children }) => (
<div>
{
isOpened &&
{ children }
}
</div>
)
If this helps you make sure to mark it as a good response!

React-router-dom routing to a new page with props

My question might seem stupid because I don't have enough background in React JS.
I have this component:
import { BrowserRouter, Route, NavLink } from "react-router-dom";
import CourseInfo from "./CourseInfo";
import OneCourse from "./OneCourse";
class CourseList extends Component {
render() {
return (
<div>
<BrowserRouter>
<div className="row courses">
{this.props.corses.map(course => (
<NavLink key={course._id} to={`/courses/profile/${course._id}`}>
<OneCourse course={course} />
</NavLink>
))}
</div>
<Route
exact
path={`/courses/profile/:id`}
render={({ match }) => (
<CourseInfo
index={match.params.id}
course={
this.props.corses.filter(el => el._id === match.params.id)[0]
}
/>
)}
/>
</BrowserRouter>
</div>
);
}
}
When I click on OneCourse component, it shows me the CourseList component in the same page with the component CourseInfo added in the bottom.
How can I send user to a new page containing only CourseInfo, knowing that I have parameters to send from this component to CourseInfo?
I want to show CourseInfo component in a different page that doesn't contain the CourseList
<NavLink> is just fine. For the rest, you might use the <Switch> component:
import { BrowserRouter, Route, NavLink, Switch } from 'react-router-dom';
class CourseList extends Component {
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route
exact
path={`/`} render={({ match }) => (
<div className="row courses">
{this.props.corses.map(course => (
<NavLink key={course._id} to={`/courses/profile/${course._id}`}>
<OneCourse course={course} />
</NavLink>
))}
</div>
)}
/>
<Route
exact
path={`/courses/profile/:id`}
render={({ match }) => (
<CourseInfo
index={match.params.id}
course={
this.props.corses.filter(el => el._id === match.params.id)[0]
}
/>
)}
/>
</Switch>
</BrowserRouter>
</div>
);
}
}
Quoting from the docs:
Switch is unique in that it renders a route exclusively. In contrast, every that matches the location renders inclusively.
With complex routing the render props approach gets confusing. Moving the routing to a separate component is a better approach:
import React from 'react';
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";
import CourseList from './CourseList';
import Course from './Course';
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={CourseList} />
<Route exact path="/courses/profile/:id" component={Course} />
</Switch>
</BrowserRouter>
);
}
export default App;
Then your CourseInfo component looks like:
class CourseList extends Component {
render() {
return (
<div>
<div className="row courses">
{this.props.corses.map(course => (
<NavLink key={course._id} to={`/courses/profile/${course._id}`}>
<OneCourse course={course} />
</NavLink>
))}
</div>
</div>
);
} }
The official documentation provides plenty examples.

React Router: Route defined in child component not working

I'm working on a React web application using React router.
The Route objects defined on the main wrapper component are working just fine, but if I try to define a Route on a child component, any link pointing to it won't be able to render the desired component.
Here is a code snippet trying to explain the situation:
class MainWrapper extends Component {
render() {
return (
<div className="App">
<Switch>
<Route exact path="/a" component= {A}/>
</Switch>
</div>
);
}
}
const A = () => {
return (
<div>
<Route exact path="/b" component={B}/>
<Link to="/b"/>
</div>
)
}
const B = () => {
return (<div>HELLO</div>)
}
In my application, the link pointing to "/b" is not rendering the B component, like the component prop weren't passed
Why this won't work?
You are specifying "exact path" in both Routes, so for rendering B your path should be exactly "/b", but when linking to "/b" component A will unmount because for rendering A you must be on exact path "/a". You should change your approach. One would be removing "exact" and including "/a" to your Link:
class MainWrapper extends Component {
render() {
return (
<div className="App">
<Switch>
<Route path="/a" component= {A}/>
</Switch>
</div>
);
}
}
const A = () => {
return (
<div>
<Route path="/b" component={B}/>
<Link to="/a/b"/>
</div>
)
}
const B = () => {
return (<div>HELLO</div>)
}
if B is a child of A, the url should be /a/b instead of /b, so you just need to update the A component with this code
const A = ({match}) => {
return (
<div>
<Route exact path={`${match.url}/b`} component={B}/>
<Link to=to={`${match.url}/b`}/>
</div>
)
};
See the documentation here
Do you have a Router somewhere? Also, you haven't closed your Link tag.
You need to wrap it in a Switch, and you should remove the exact prop from your /b route.
const A = ({match}) => {
return (
<div>
<Switch>
<Route path={`${match.url}/b`} component={B}/>
</Switch>
<Link to="a/b"/>
</div>
)
}

Resources