I am doing a simple application which has a protected route.
The code goes like:
function App() {
return (
<>
<Router>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/protected"
element={
<PrivateRoute>
<ProtectedExample />
</PrivateRoute>
}
/>
<Route path="/sign-in" element={<Login />} />
</Routes>
</Router>
</>
);
}
Private Route is :
import { Navigate } from "react-router-dom";
import { useAuthStatus } from "../hooks/useAuthStatus";
import Spinner from "./Spinner";
const PrivateRoute = ({ children }) => {
const { loggedIn, checkingStatus } = useAuthStatus();
if (checkingStatus) {
return <Spinner />;
}
return loggedIn ? children : <Navigate to="/sign-in" />;
};
export default PrivateRoute;
Protected Route
import React from "react";
const ProtectedExample = () => {
return <div>ProtectedExample</div>;
};
export default ProtectedExample;
Header.js
import React from "react";
import { Container, Nav, Navbar } from "react-bootstrap";
import { Link } from "react-router-dom";
const Header = () => {
return (
<Navbar bg="light" variant="light" fixed="top">
<Container>
<Navbar.Brand>
<Link to="/">Vivans</Link>
</Navbar.Brand>
<Nav className="me-auto">
<Link to="/sign-in" className="me-4">
Login
</Link>
<Link to="/protected">ProtectedRoute</Link>
</Nav>
</Container>
</Navbar>
);
};
export default Header;
When i remove the header component in App.js
The output is :
But when i add header component in App.js
The output is :
It doesn't return ProtectedExample as expected to be .
Why?
Actually, it might be rendering it. It maybe invisible for us because of CSS styling.
<Navbar bg="light" variant="light" fixed="top">
In the above line, you have given the NavBar to be fixed at the top. So the ProtectedExample would be behind the NavBar
Refer: https://developer.mozilla.org/en-US/docs/Web/CSS/position
Try changing your component like this,
const ProtectedExample = () => {
return <div style="margin-top: 3em">ProtectedExample</div>;
};
This might bring the content downwards to make it visible.
Related
here I am building a website and I am using react-router-dom
everything seems to work fine when I use the Navbar and Footer components in every page.
So I come up with idea to wrap the component in a wrapper which contains Navbar and Footer.
When I click on Link or maybe NavLink, it seems to work fine but when I click again on same or another link in navbar then it navigates to a sub path under the previous selected path.
like this
on Single Click:
http://localhost:3000/projects
on clicking the same link again:
http://localhost:3000/projects/projects
App.js
import './App.css';
import {BrowserRouter as Router, Route, Outlet, Routes} from 'react-router-dom'
import {CommonScreen, Wrapper, About, Email, Projects} from './Barell'
function App() {
return (
<>
<Router>
<Routes>
<Route index element={<Wrapper children={<CommonScreen/>}/>}/>
<Route path='about' element={<Wrapper children={<About/>}/>}/>
<Route path='projects' element={<Wrapper children={<Projects/>}/>}/>
<Route path='email' element={<Email/>}/>
</Routes>
</Router>
<Outlet/>
</>
);
}
export default App;
Navbar.jsx:
import React from 'react'
import '../../index.css'
import { NavLink } from 'react-router-dom'
const links = [
{
name:"Home",
slug:"/",
},
{
name:"Projects",
slug:"projects",
},
{
name:"About",
slug:"about",
},
{
name:"Contact",
slug:"email",
},
]
export const Navbar = () => {
let activeStyle = {
textDecoration: "underline",
};
return (
<>
<nav>
<div id='brand'>Prema<span>culture</span></div>
<ul id='links'>
{
links.map((current,index) => (
<li>
<NavLink
key={index}
to={current.slug}
style={({ isActive }) =>
isActive ? activeStyle : undefined
}
>
{current.name}
</NavLink>
</li>
))
}
</ul>
</nav>
</>
)
}
Wrapper.jsx:
import React from 'react'
import { Navbar, Footer } from '../Barell'
export const Wrapper = ({children}) => {
return (
<>
<Navbar/>
{children}
<Footer/>
</>
)
}
You are using relative paths (versus absolute), so when already on a route, say "/projects", and then click a link that navigates to "about", the code links relative from "/projects" and results in "/projects/about".
To resolve you can make all the link paths absolute by prefixing a leading "/" character. Absolute paths begin with "/".
Example:
const links = [
{
name:"Home",
slug:"/",
},
{
name:"Projects",
slug:"/projects",
},
{
name:"About",
slug:"/about",
},
{
name:"Contact",
slug:"/email",
},
];
Additionally, to make the code more DRY you might also want to convert the Wrapper component into a layout route. Layout routes render an Outlet component for nested routes to be rendered into instead of a children prop for a single routes content.
Example
import React from 'react';
import { Outlet } from 'react-router-dom';
import { Navbar, Footer } from '../Barell';
export const Layout = () => {
return (
<>
<Navbar/>
<Outlet />
<Footer/>
</>
)
}
...
function App() {
return (
<>
<Router>
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<CommonScreen />} />
<Route path='about' element={<About />} />
<Route path='projects' element={<Projects />} />
</Route>
<Route path='email' element={<Email />} />
</Routes>
</Router>
<Outlet />
</>
);
}
I have some problem with react-router. It doesn't go to Edit page when I click on id. The id is in URL, but it doesn't do anything.
const Main = (props) => {
const { pathname } = props.location;
return (
<Fragment>
<div>
<div className="container">
<Header />
{pathname === "/create" ? <Create /> : null}
{pathname === '/edit/:id' ? <Edit /> : null}
{pathname === "/" ? <Home /> : null}
</div>
</div>
</Fragment>
);
};
export default withRouter(Main);
app.js:
require('./components/Index');
import React from 'react'
import ReactDOM from "react-dom"
import Index from "./components/Index";
import { BrowserRouter as Router} from "react-router-dom";
import { ToastContainer } from 'react-toastify';
const App =() =>{
}
if (document.getElementById('app')) {
ReactDOM.render(<Router> <Index /> <ToastContainer /></Router>, document.getElementById('app'));
}
index.js:
import React from "react";
import { Route, Switch } from "react-router-dom";
import Main from "./CRUD/Main";
import Create from "./CRUD/Create";
import Edit from "./CRUD/Edit";
import Home from "./CRUD/Home";
const Index = (props) => {
return (
<Main>
<Switch>
<Route path="/Create" component={Create} />
<Route path='/edit/:id' component={Edit} />
<Route exact path="/" component={Home} />
</Switch>
</Main>
);
};
export default Index;
I think main.js have some problems with pathname.
You don't need to do conditional rendering like you are doing in Main when using react-router. Switch will automatically render the first child <Route> or <Redirect> that will match the location.
Hence, you need to remove Main from your router component i.e. Index so that it looks like as shown below:
const Index = (props) => {
return (
<>
<NavBar /> {/* NavBar is optional, I just added for example */}
<Switch>
<Route path="/create" component={Create} />
<Route path="/edit/:id" component={Edit} />
<Route exact path="/" component={Home} />
</Switch>
</>
);
}
NavBar: (Links; just for example)
function NavBar() {
return (
<ul>
<li>
<Link to="/create">Go to create</Link>
</li>
<li>
<Link to="/edit/123">Go to edit with id = 123</Link>
</li>
<li>
<Link to="/">Go to home</Link>
</li>
</ul>
);
}
Now, when you click on the above links, it will automatically take you to the related component (as declared in routes i.e. Index). (no manually condition checking)
And example, to retrieve the URL param i.e. id in Edit component using useParams hook:
function Edit() {
const { id } = useParams<{ id: string }>(); // Remove <{ id: string }> if not using TypeScript
return <h2>Edit, {id}</h2>;
}
I can get the data of the players when i Route to the Players Component,
but when i click on the Link Tags, the PLayersContainer Component is not opening.
This is my App.js File.
import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import Players from './Components/Players'
import PlayersContainer from './Components/Container/playersContainer'
import Navigation from './Components/Navigation';
export default class App extends Component {
state = {
players:[
{
id:1,
name:'Ronaldo'
},
{
id:2,
name:'Messi'
}
]
}
render() {
return (
<Router>
<Navigation />
<Switch>
<Route path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route exact path="/players/:id" render={PlayersContainer} />
</Switch>
</Router>
)
}
}
This is my Players Component.
import React from 'react'
import { Link } from 'react-router-dom'
export default function Players(props) {
const renderPlayers = () => {
let players = props.players.map(playerObj => <li> <Link to={`/players/${playerObj.id}`}> Player {playerObj.name} </Link></li>)
return players
}
return (
<div>
<ul>
{renderPlayers()}
</ul>
</div>
)
}
This is my PlayersContainer Component, where i want to render the individual data of the Player.
import React from 'react'
import { Link } from 'react-router-dom'
export default function PlayersContainer(props) {
const renderPlayers = () => {
console.log(props);
}
return (
<div>
<ul>
{renderPlayers()}
</ul>
</div>
)
}
You have the wrong Route marked as exact. The way its written currently, anything beginning with /players will match the first route. Since its in a switch, only the first match will be rendered.
Change it from:
<Route path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route exact path="/players/:id" render={PlayersContainer} />
to this:
<Route exact path="/players" render={(props) => <Players {...props} players={this.state.players} />} />
<Route path="/players/:id" render={PlayersContainer} />
Now only exactly /players will match the first route, and /players/id can continue past it to match the second.
i'm trying to change text in p depends on which Route is enabled. I was trying to make this with switch statement, but honestly don't know how, any ideas?
import React from 'react';
import '../styles/Main.css'
import { Switch, Route } from 'react-router-dom';
import DaysWeather from '../pages/DaysWeather';
import WorldWeather from '../pages/WorldWeather'
import CurrentWeather from '../pages/CurrentWeather';
const Main = () => {
return (<>
<main>
<p>Sprawdź pogodę w swoim mieście</p>
<Switch>
<Route path="/" exact component={CurrentWeather} />
<Route path="/daysweather" exact component={DaysWeather} />
<Route path="/worldweather" exact component={WorldWeather} />
</Switch>
</main>
</>);
}
export default Main;
I think you were on to the right idea using a switch statement. This is my implementation:
/* Main.js */
import React, { useState, useEffect } from "react";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import "./styles.css";
import WorldWeather from "./components/WorldWeather";
import DaysWeather from "./components/DaysWeather";
import CurrentWeather from "./components/CurrentWeather";
function getParaText() {
const route = window.location.pathname;
switch (route) {
case "/":
return "Current Weather Header";
case "/daysweather":
return "Days Weather Header";
case "/worldweather":
return "World Weather Header";
}
}
export default function Main() {
const [paraText, setParaText] = useState(getParaText());
const changeOnNewRoute = () => {
setParaText(getParaText());
};
return (
<main>
<BrowserRouter>
<p>{paraText}</p>
<div>
<Link style={{ marginRight: "20px" }} to="/">
CurrentWeather
</Link>
<Link style={{ marginRight: "20px" }} to="/daysweather">
DaysWeather
</Link>
<Link style={{ marginRight: "20px" }} to="/worldweather">
WorldWeather
</Link>
</div>
<Switch>
<Route
exact
path="/"
render={props => (
<CurrentWeather changeOnNewRoute={changeOnNewRoute} />
)}
/>
<Route
exact
path="/daysweather"
render={props => (
<DaysWeather changeOnNewRoute={changeOnNewRoute} />
)}
/>
<Route
exact
path="/worldweather"
render={props => (
<WorldWeather changeOnNewRoute={changeOnNewRoute} />
)}
/>
</Switch>
</BrowserRouter>
</main>
);
}
/* Child Component */
import React, { useEffect } from "react";
const CurrentWeather = props => {
useEffect(() => {
props.changeOnNewRoute();
});
return <div>This is the CurrentWeather Component</div>;
};
export default CurrentWeather;
Edit: added the final implementation with state for future use
You can access to current route with props.location.pathname
<Router>
<div>
<Nav />
<hr />
<Switch>
<Route path="/" exact component={CurrentWeather} />
<Route path="/daysweather" exact component={DaysWeather} />
<Route path="/worldweather" exact component={WorldWeather} />
</Switch>
</div>
</Router>
With withRouter,You can get access to the history object’s properties
function Nav(props) {
return (
<div>
<h2>
Current Route : {props.location.pathname}
</h2>
</div>
);
}
export default withRouter(Nav);
look at this sample,could be helpfull
Hello I'm having a problem with setting 'react-router-modal' parentPath for the last used component
ModalRoute and ModalContainer are a part of
react-router-modal
App.js
class App extends Component {
render() {
return (
<main>
<Navbar />
<BrowserRouter>
<div>
<Switch>
<Route path="/main" component={ContentDefault} />
<Route path="/search" component={SearchScreen} />
<ModalRoute
path="/register"
parentPath="/"
component={RegisterScreen}
/>
<Route path="/contact" component={ContentDefault} />
<Route component={ContentDefault} />
</Switch>
<ModalContainer />
</div>
</BrowserRouter>
<Footer />
</main>
);
}
}
export default App;
SearchScreen.jsx
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
class SearchScreen extends Component {
render() {
return (
<main>
<h1>SearchScreen</h1>
</main>
);
}
}
export default withRouter(SearchScreen);
For Example i'm on mainScreen then i move to the SearchScreen then i open modal from navbar. I need my modal to go back to the SearchScreen
I found couple of solutions which may help you.
You can try with this:
Create state prevPath
Add componentWillReceiveProps
Provide prevPath state to the parentPath with condition if prevPath is empty redirect me on route '/'
class App extends Component {
state = {
prevPath: ''
}
componentWillReceiveProps(nextProps) {
if (nextProps.location !== this.props.location) {
this.setState({ prevPath: this.props.location })
}
}
<ModalRoute
path="/register"
parentPath={this.state.prevPath || '/'}
component={RegisterScreen}
/>
This is one of the solutions we can try later another one if this doesn't help.