React Router Link Persists after Page Navigation - javascript

I have managed to get my react-router to navigate to another page on my website however after it's done redirecting the link or button in this case still persists on the other page (login.js).
Can someone explain to me why this happens, and how I get it to not render on my page?
App.js
import Login from "./login";
import React from "react";
import "./App.css";
import { Routes, Route, Router } from "react-router-dom";
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
const handleClick = () => {
navigate("/login");
}
return (
<div className="Main">
<Routes>
<Route path="/login" element={<Login />} />
</Routes>
<button onClick={handleClick} type="button" />
</div>
);
}
export default App;
Login.js
[![import React from "react";
export default function Login() {
return (
<div className="Login">
<h1>Login</h1>
</div>
)}

If you clearly look your routes are working inside a div which means anything along with routes will be shown on the page permanently even if your route changes.

Related

React Router Dom (v 6.4.3) page becomes unresponsive after navigation

My page is becoming unresponsive when I try to navigate using useNavigate or a Link in my component. After clicking a button or link, the url will change, and the javascript inside the target component will execute, but the page does not re-render. useNavigate works with other components in my app, so I'm not quite sure what the issue is. Any input would be appreciated!
Dashboard.js:
import { useNavigate, Link } from "react-router-dom";
const Dashboard = () => {
console.log("hello from dashboard");
let navigate = useNavigate();
return (
<div>
<Link className="pl-20" to="/test">
test
</Link>
</div>
);
};
export default Dashboard;
Test.js:
import React from "react";
const Test = () => {
console.log("hello from test page");
return <div>this is the test page</div>;
};
export default Test;
App.js:
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Dashboard from "./pages/Dashboard";
import Test from "./pages/Test";
function App() {
return (
<>
<Nav />
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/test" element={<Test />} />
</Routes>
</>
);
}
gif showing the issue
you gave pl-20 class on Dashboard. but not on Test component.
Please check.

React routing using components (PageRenderer)

I'm trying to do basic routing in React. Usually what I have done, and what I will mention later on, is use element={<some page>}. But currently I want to learn and experiment what other options there are, so I came across components where you insert a function. I have followed a tutorial and I did the exact same, except the tutorial uses an older version of router dom so it doesn't use Routes.
Here is the code:
App.js:
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'
import Register from './pages/register';
import Login from './pages/login';
import PageRender from './PageRender';
function App() {
return (
<Router>
<input type='checkbox' id='theme'/>
<div className="App">
<div className="main">
<Routes>
<Route exact path="/:page" component={PageRender}/>
<Route exact path="/:page/:id" component={PageRender}/>
</Routes>
</div>
</div>
</Router>
);
}
export default App;
PageRender.js:
import React from 'react'
import { useParams } from 'react-router'
import NotFound from './components/NotFound'
const generatePage = (pageName) => {
const component = () => require(`./pages/${pageName}`).default
try {
return React.createElement(component())
} catch (err) {
return <NotFound />
}
}
const PageRender = () => {
const {page, id} = useParams()
let pageName = "";
if(id){
pageName = `${page}/[id]`
}else{
pageName = `${page}`
}
return generatePage(pageName)
}
export default PageRender
The login and register js are just basic arrow functions which display login or register (still didn't come to that part). What I want to do is when I enter the url, let's say for instance: http://localhost:3000/register, it sends me to register page and if I enter a wrong path it will send me to the "NotFound" page. But sadly, it doesn't work. I know I can work around this problem if I simply do this:
<Route exact path="/login" element={<Login/>}/>
This method works, but currently I'm in the process of learning and I'm curious why this method didn't work.
I was able to get your code working in react-router-dom v5, the trick was importing the components once in App so they are built/transpiled. The PageRender component worked as-is.
RRDv5
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import './pages/register';
import "./pages/login";
import PageRender from "./PageRender";
export default function App() {
return (
<Router>
<input type="checkbox" id="theme" />
<div className="App">
<div className="main">
<Switch>
<Route path="/:page/:id" component={PageRender} />
<Route path="/:page" component={PageRender} />
</Switch>
</div>
</div>
</Router>
);
}
RRDv6 - Swap the Switch component to the Routes component, and switch to using the element prop instead of the component prop to render the PageRender component as JSX.
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import './pages/register';
import "./pages/login";
import PageRender from "./PageRender";
export default function App() {
return (
<Router>
<input type="checkbox" id="theme" />
<div className="App">
<div className="main">
<Routes>
<Route path="/:page/:id" element={<PageRender />} />
<Route path="/:page" element={<PageRender />} />
</Routes>
</div>
</div>
</Router>
);
}

Nesting a react router inside another component while preserving a sidebar

As an exercise, I'm making a react app (still learning React) that implements a login system with firebase. Of course, to implement such a feature, react router is necessary and I have successfully implemented it. However, once the user logs in he should be able to see a sidebar alongside other content that is changed dynamically. I now need to again use react router to change those pages when a user clicks on a specific item in the sidebar without having to render the sidebar with each component. I have read the docs for nesting routers but just cant get it to work. Any help is greatly appreciated.
Here's the code:
App.js:
import "./App.css";
import LoginForm from "./components/LoginForm";
import { AuthProvider } from "./contexts/AuthContext";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Dashboard from "./components/Dashboard";
import PrivateRoute from "./components/PrivateRoute";
function App() {
return (
<div className="App">
<Router>
<AuthProvider>
<Switch>
<PrivateRoute exact path="/" component={Dashboard} />
<Route path="/login" component={LoginForm} />
</Switch>
</AuthProvider>
</Router>
</div>
);
}
export default App;
Dashboard.js:
import React from "react";
import { useAuth } from "../contexts/AuthContext";
import { useHistory } from "react-router";
import Sidebar from "./Sidebar/Sidebar";
import { useRouteMatch } from "react-router";
const Dashboard = () => {
const { currentUser, logout } = useAuth();
const history = useHistory();
let { path, url } = useRouteMatch();
const handleLogout = async () => {
try {
await logout();
history.push("/login");
} catch (error) {
console.log(error);
}
};
if (!currentUser) return null;
return (
<div>
<Sidebar logout={handleLogout} />
</div>
);
};
export default Dashboard;
PS. I'm quite new to react and any tip/critique is welcome
You can always conditionally render the sidebar.
function Sidebar() {
const { currentUser } = useAuth()
if (!currentUser) return null
// ...
}
Within your App component, just render the Sidebar component outside of the Switch:
function App() {
return (
<div className="App">
<Router>
<AuthProvider>
<Sidebar />
<Routes />
</AuthProvider>
</Router>
</div>
);
}
function Routes() {
const { currentUser } = useAuth()
return (
<Switch>
{currentUser && <PrivateRoutes />}
<PublicRoutes />
</Switch>
)
}
Basically all you need to do is render the sidebar on all routes. If you need to render custom Sidebar content based off of routes, you can add another Switch within Sidebar. You can add as many Switch components as you want as long as they are within your Router.
Even though i understand what your trying to do, i don't think you should mind put the sidebar inside the component.
React is powerfull enough to cache a lots of stuffs and disable unnecessary renders. I think the path you should go its figure out how to use wisely useCallback useMemo, memo and make all the tricks to prevent re-renders inside the sidebar components. This way you can reuse the sidebarcomponent, or any component, without to think about location.

Redirecting using React Router shows blank page

I'm trying to build a simple example project where the user is redirected to the 'contact' page upon clicking a button, using React. I'm trying to achieve this by setting the value of a state property. When I run the code I have, it does change the browser address bar URL to that of the contact page, but does not seem to actually load the component - I get a blank page instead. If I manually navigate to that URL (http://localhost:3000/contact) I can see the contents.
Here are my App.js and Contact.js files -
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import Contact from './Contact';
class App extends Component {
state = {
redirect: false
}
setRedirect = () => {
this.setState({
redirect: true
})
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/contact' />
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/contact' component={Contact} />
</Switch>
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
</Router>
)
}
}
export default App;
Contact.js
import React, { Component } from 'react';
class Contact extends Component {
render() {
return (
<div>
<h2>Contact Me</h2>
<input type="text"></input>
</div>
);
}
}
export default Contact;
Using state isn't really a requirement for me, so other (preferably simpler) methods of redirection would be appreciated too.
Since your button is nothing more than a link, you could replace it with:
<Link to="/contact">Redirect</Link>
There are many alternatives though, you could for example look into BrowserRouter's browserHistory:
import { browserHistory } from 'react-router'
browserHistory.push("/contact")
Or perhaps this.props.history.push("/contact").
There are pros and cons to every method, you'll have to look into each and see which you prefer.
I got here for a similiar situation. It's possible use withRouter (https://reactrouter.com/web/api/withRouter) to handle that.
This example was tested with "react": "^16.13.1","react-router-dom": "^5.2.0" and "history": "^5.0.0" into "dependecies" sections in package.json file.
In App.js I have the BrowserRouter (usually people import BrowserRouter as Router, I prefer work with original names) with Home and Contact.
App.js
import React, { Component } from "react";
import {
BrowserRouter,
Switch,
Route,
} from "react-router-dom";
import Home from "./pages/Home";
import Contact from "./pages/Contact";
class App extends Component
{
// stuff...
render()
{
return (
<BrowserRouter>
<Switch>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
);
}
}
export default App;
ASIDE 1: The Route with path="/contact" is placed before path="/" because Switch render the first match, so put Home at the end. If you have path="/something" and path="/something/:id" place the more specific route (with /:id in this case) before. (https://reactrouter.com/web/api/Switch)
ASIDE 2: I'm using class component but I believe (I didn't test it) a functional component will also work.
In Home.js and Contact.js I use withRouter associated with export keyword. This makes Home and Contact components receive the history object of BrowserRouter via props. Use method push() to add "/contact" and "/" to the history stack. (https://reactrouter.com/web/api/history).
Home.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Home = ( props ) =>
{
return (
<div>
Home!
<button
onClick={ () => props.history.push( "/contact" ) }
>
Get in Touch
<button>
</div>
);
}
export default withRouter( Home );
Contact.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<button
onClick={ () => props.history.push( "/" ) }
>
Go Home
<button>
</div>
);
}
export default withRouter( Contact );
Particularly, I'm using also in a BackButton component with goBack() to navigate backwards:
BackButton.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const BackButton = ( props ) =>
{
return (
<button
onClick={ () => props.history.goBack() }
>
Go back
<button>
);
}
export default withRouter( BackButton );
So I could modify the Contact to:
Contact.js (with BackButton)
import React from "react";
import BackButton from "../components/BackButton";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<BackButton />
</div>
);
}
export default Contact; // now I'm not using history in this file.
// the navigation responsability is inside BackButton component.
Above was the best solution for me. Other possible solutions are:
useHistory Hook (https://reactrouter.com/web/api/Hooks)
work with Router instead BrowserRouter - (https://reactrouter.com/web/api/Router)

how to change exact path after successful login

i am making a login page that redirect the user after a successful login to home page i am using react router dom i tried to look for a simple way to do it but i couldn't find :
import Authen from './Pages/Authen';
import Home from './Pages/Home';
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom';
class App extends Component {
render() {
return (
<div className="App">
<Router>
<div>
<ul>
</ul>
<Route exact path="/" component={Authen}/>
<Route path="Home" component={Home}/>
</div>
</Router>
</div>
thank you for your help i really appreciate it :)
login page that redirect the user after a successful login to home
page
Use the withRouter higher order component that comes with react-router-dom. It will give your component acess to the history prop. With the history prop you can push to any new URL.
import React from 'react'
import {withRouter} from 'react-router-dom'
class Authen extends React.Component {
onLogin = () => {
// also other authentication code
this.props.history.push('/home')
}
render() {
return (
<button onClick={() => this.onLogin()}> Login </button>
)
}
}
export default withRouter(Authen);

Categories

Resources