React Routing Redirect onClick - javascript

Ive been trying to do a simple redirect to another component on button click, but for some reason it doesnt work.I want to redirect to '/dashboard' and display AdminServices from login as follows:
//index.js
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>,
document.getElementById("root"));
//App.js
<Switch>
<Route path="/" component={Login} />
<Route path="/dashboard" component={AdminServices} />
</Switch>
//Login.js
<Button
onClick={this.login}
>
</Button>
login = () =>{
<Redirect to="/dashboard" />
}
//AdminServices.js
render(){
return(
<div>Test</div>
);
}

Instead of using the props.history, a better way is using the useHistory hook like below:
import { useHistory } from 'react-router-dom'
const MyComponent = (props) => {
const history = useHistory();
handleOnSubmit = () => {
history.push(`/dashboard`);
};
};

In react-router-dom v6 you can use useNavigate(), as answered here.
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/dashboard');
if you're still using previous versions, you can use useHistory, as Tellisense mentioned.
import { useHistory } from 'react-router-dom';
const navigate = useHistory();
navigate.push('/dashboard');

You can route by function by like this
handleOnSubmit = () => {
this.props.history.push(`/dashboard`);
};
Hope this help

Simple, use NavLink instead of button
import { NavLink } from 'react-router-dom'
<NavLink to="/dashboard"> Dashboard </NavLink>
https://reacttraining.com/react-router/web/api/NavLink

<Switch> component render only the first route that matches. You just need to swap your <Route> components in <Switch> case and use #Adnan shah answer to make redirect function.
P.S
react router auth example

This happened to me once I had missed importing the component to the route.js file.
You need to reference/import files from the component to the routefile.

Use an a tag
<a href="/" />

Maybe this might help
import { useHistory } from "react-router-dom";
const history = useHistory();
<Button
onClick={this.login}
>
</Button>
login = () =>{
history.push("/about");
}

With react-router-dom v6 you will need to use useNavigate instead of useHistory:
import { useNavigate } from "react-router-dom";
export default function MyComponent(){
const navigate = useNavigate();
handleOnClick = () => {
navigate("/dashboard");
};
};

Related

Render to another functional component based on route

I have routes.js -
import React from 'react'
import { Switch, Route , Link } from 'react-router';
import {BrowserRouter, Router} from 'react-router-dom';
import SignIn from './Components/Login/SignIn';
import MainPage from './Components/MainPage';
function Routes() {
return (
<Router>
<Switch>
<Route path="/logout" component={SignIn} ></Route>
<Route path="/home" component={MainPage} ></Route>
</Switch>
</Router>
)
}
export default Routes
Within SignIn.js I have button from which I want to render to main page.
Main bits of SignIn.Js are as below -
import MainPage from '../MainPage';
import { useHistory } from 'react-router-dom';
<FormGroup>
<Button style={{width:'100%',backgroundColor:"#FCB724",color:"black",fontWeight:"bold"}} onClick={NavigateToMainPage} >Sign in using our secure server</Button>
</FormGroup>
function NavigateToMainPage(){
let path = `/home`;
let history = useHistory();
history.push(path);
}
This is not navigating.
How can I navigate on button click to another component in this case ?
I'm not sure how this code isn't breaking completely because you can't use a hook outside of a react component, you can't use it in a normal function.
const Form = () => {
const history = useHistory()
return <form>
<button onClick={() => history.push('/home')}>Example</button>
</form>
}
You want something like that.
You have to change how you call a function inside an on click prop.
If you write onClick={functionName}, the functionName would be always executed when the component is rendered and it would produce an infinite loop.
You have to change it into this onClick={() => functionName()} means that the functionName will be executed only after the the onclick is executed.
Don't forget to add return to the component.
Also if you use hook (like something starts from use word), you have to put it inside the functional component.
Here is the full code:
import MainPage from '../MainPage';
import { useHistory } from 'react-router-dom';
const SignIn = () => {
const NavigateToMainPage = () => {
let path = `/home`;
let history = useHistory();
history.push(path);
}
return(
<FormGroup>
<Button
style={{width:'100%',backgroundColor:"#FCB724",color:"black",fontWeight:"bold"}}
onClick={() => NavigateToMainPage()}
>
Sign in using our secure server
</Button>
</FormGroup>
)
}
export default SignIn
Additional info:
I personally use const (ES6 arrow function https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) rather than function to declare a function.
In the MainPage.js, you can directly import the Route, Switch, and Link from react-router-dom.
So you can change it from this:
import {Switch, Route , Link} from 'react-router';
import {BrowserRouter, Router} from 'react-router-dom';
into this:
import {BrowserRouter, Router, Switch, Route, Link} from 'react-router-dom';
I'd implement your example using NavLink component instead of useHistory hook.
import MainPage from '../MainPage';
import { NavLink} from 'react-router-dom';
<FormGroup>
<NavLink to="/home" style={{width:'100%',backgroundColor:"#FCB724",color:"black",fontWeight:"bold"}} onClick={NavigateToMainPage}>Sign in using our secure server</NavLink>
</FormGroup>
To top it off, please use const variables, forget about var.

How to redirect in React on click?

In my react app, I am using the useHistory hook to redirect to my Home Component. Why is useHistory undefined, and how would I solve this problem?
App.js
import 'bootstrap/dist/css/bootstrap.css'
import React from "react";
import { Button } from 'react-bootstrap';
import { BrowserRouter, Route, useHistory} from 'react-router-dom';
import { Switch } from 'react-router-dom/cjs/react-router-dom.min';
import './App.css';
import Home from './components/Home/Home';
const App = () =>
{
const history = useHistory()
const handleClick = () =>
{
console.log(history)
console.log("handle click")
// history.push("/home") this line throws "cannot read property of undefined" error
}
return (
<>
<BrowserRouter>
<Button onClick = {handleClick} variant = "primary">Get Started</Button>
<Switch>
<Route path="/home" exact component = {Home}/>
</Switch>
</BrowserRouter>
</>
);
}
export default App;
Try to use the useHistory() outside this component and see it work. It seems you are calling the function before the Router itself and both are imported from react-router-dom .

React router goBack() not working properly

I have faced a problem. I am used react-router-dom for routing. It's working well but goBack is not working properly. When I clicked back button it's 1st going to NotFound/Signin page then redirect to back page. How can I overcome this issue?
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import Signin from '../ui/signin/Signin';
import AddEvent from '../ui/events/AddEvent';
import EventView from '../ui/events/EventView';
import NotFound from '../ui/NotFound';
const history = createBrowserHistory();
const privatePages = [
'/events',
'/addevent',
];
const publicPages = ['/', '/signup','/forgotpassword'];
const onEnterPublicPage = () => {
if (Meteor.userId()) {
history.replace('/events');
}
};
const onEnterPrivatePage = () => {
if (!Meteor.userId()) {
history.replace('/');
}
};
export const onAuthenticationChange = (isAuthenticated) => {
const pathname = this.location.pathname;
const isUnauthenticatedPage = publicPages.includes(pathname);
const isAuthenticatedPage = privatePages.includes(pathname);
if (isAuthenticated && isUnauthenticatedPage) {
history.replace('/events');
} else if (!isAuthenticated && isAuthenticatedPage) {
history.replace('/');
}
}
export const routes = (
<Router history = {history}>
<Switch>
<Route
exact path="/events"
component={ListEvents}
onEnter={onEnterPrivatePage} />
<Route
exact path="/addevent"
component={AddEvent}
onEnter={onEnterPrivatePage} />
<Route component={NotFound}/>
<Route
exact path="/"
component={Signin}
onEnter={onEnterPublicPage} />
</Switch>
</Router>
);
In the component :
constructor(props){
super(props);
this.goBack = this.goBack.bind(this);
}
goBack(){
this.props.history.goBack();
// this.props.history.push.go(-1);
}
In the return
<Link
to=""
onClick={this.goBack}
className="back-icon">
Back
</Link>
Its because you are using history.replace('/'). You are replacing, not pushing so there is no previous route.
One possible way is, Instead of using Link, use history.push to change the route dynamically. To achieve that remove the Link component and define the onClick event on "li" or "button". Now first perform all the task inside onClick function and at the end use history.push to change the route means to navigate on other page.
I hope this helps
I have had the same issue and the history.goBack() function doesn't work with <Link /> component, but if you replace it for any other it will work

react-router (v4) how to go back?

Trying to figure out how can I go back to the previous page. I am using [react-router-v4][1]
This is the code I have configured in my first landing page:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={Page1}/>
<Route path="/Page2" component={Page2}/>
<Route path="/Page3" component={Page3}/>
</div>
</Router>
In order to forward to subsequent pages, I simply do:
this.props.history.push('/Page2');
However, how can I go back to previous page?
Tried few things like mentioned below but no luck:
1. this.props.history.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.props')
this.context.router.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.context')
this.props.history.push('/');
Gives error:
TypeError: null is not an object (evaluating 'this.props')
Posting the Page1 code here below:
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
class Page1 extends Component {
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
}
handleNext() {
this.props.history.push('/page2');
}
handleBack() {
this.props.history.push('/');
}
/*
* Main render method of this class
*/
render() {
return (
<div>
{/* some component code */}
<div className="navigationButtonsLeft">
<Button onClick={this.handleBack} bsStyle="success">< Back</Button>
</div>
<div className="navigationButtonsRight">
<Button onClick={this.handleNext} bsStyle="success">Next ></Button>
</div>
</div>
);
}
export default Page1;
I think the issue is with binding:
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
goBack(){
this.props.history.goBack();
}
.....
<button onClick={this.goBack}>Go Back</button>
As I have assumed before you posted the code:
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this); // you are missing this line
}
UPDATED:
Now we have hook, so we can do it easily by using useHistory
const history = useHistory()
const goBack = () => {
history.goBack()
}
return (
<button type="button" onClick={goBack}>
Go back
</button>
);
ORIGINAL POST:
this.props.history.goBack();
This is the correct solution for react-router v4
But one thing you should keep in mind is that you need to make sure this.props.history is existed.
That means you need to call this function this.props.history.goBack(); inside the component that is wrapped by < Route/>
If you call this function in a component that deeper in the component tree, it will not work.
EDIT:
If you want to have history object in the component that is deeper in the component tree (which is not wrapped by < Route>), you can do something like this:
...
import {withRouter} from 'react-router-dom';
class Demo extends Component {
...
// Inside this you can use this.props.history.goBack();
}
export default withRouter(Demo);
For use with React Router v4 and a functional component anywhere in the dom-tree.
import React from 'react';
import { withRouter } from 'react-router-dom';
const GoBack = ({ history }) => <img src="./images/back.png" onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
Each answer here has parts of the total solution. Here's the complete solution that I used to get it to work inside of components deeper than where Route was used:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
^ You need that second line to import function and to export component at bottom of page.
render() {
return (
...
<div onClick={() => this.props.history.goBack()}>GO BACK</div>
)
}
^ Required the arrow function vs simply onClick={this.props.history.goBack()}
export default withRouter(MyPage)
^ wrap your component's name with 'withRouter()'
Here is the cleanest and simplest way you can handle this problem, which also nullifies the probable pitfalls of the this keyword. Use functional components:
import { withRouter } from "react-router-dom";
wrap your component or better App.js with the withRouter() HOC this makes history to be available "app-wide". wrapping your component only makes history available for that specific component``` your choice.
So you have:
export default withRouter(App);
In a Redux environment export default withRouter( connect(mapStateToProps, { <!-- your action creators -->})(App), ); you should even be able to user history from your action creators this way.
in your component do the following:
import {useHistory} from "react-router-dom";
const history = useHistory(); // do this inside the component
goBack = () => history.goBack();
<btn btn-sm btn-primary onclick={goBack}>Go Back</btn>
export default DemoComponent;
Gottcha useHistory is only exported from the latest v5.1 react-router-dom so be sure to update the package. However, you should not have to worry.
about the many snags of the this keyword.
You can also make this a reusable component to use across your app.
function BackButton({ children }) {
let history = useHistory()
return (
<button type="button" onClick={() => history.goBack()}>
{children}
</button>
)
}```
Cheers.
Can you provide the code where you use this.props.history.push('/Page2');?
Have you tried the goBack() method?
this.props.history.goBack();
It's listed here https://reacttraining.com/react-router/web/api/history
With a live example here https://reacttraining.com/react-router/web/example/modal-gallery
If using react hooks just do:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.go(-1);
UPDATE 2022 w V6
navigate(-1)
to omit the current page from history:
navigate(-1, { replace: true })
Try:
this.props.router.goBack()
Simply use
<span onClick={() => this.props.history.goBack()}>Back</span>
Hope this will help someone:
import React from 'react';
import * as History from 'history';
import { withRouter } from 'react-router-dom';
interface Props {
history: History;
}
#withRouter
export default class YourComponent extends React.PureComponent<Props> {
private onBackClick = (event: React.MouseEvent): void => {
const { history } = this.props;
history.goBack();
};
...
Maybe this can help someone.
I was using history.replace() to redirect, so when i tried to use history.goBack(), i was send to the previous page before the page i was working with.
So i changed the method history.replace() to history.push() so the history could be saved and i would be able to go back.
I am not sure if anyone else ran into this problem or may need to see this. But I spent about 3 hours trying to solve this issue:
I wanted to implement a simple goBack() on the click of a button. I thought I was off to a good start because my App.js was already wrapped in the Router and I was importing { BrowserRouter as Router } from 'react-router-dom'; ... Since the Router element allows me to assess the history object.
ex:
import React from 'react';
import './App.css';
import Splash from './components/Splash';
import Header from './components/Header.js';
import Footer from './components/Footer';
import Info from './components/Info';
import Timer from './components/Timer';
import Options from './components/Options';
import { BrowserRouter as Router, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Header />
<Route path='/' component={Splash} exact />
<Route path='/home' component={Info} exact />
<Route path='/timer' component={Timer} exact />
<Route path='/options' component={Options} exact />
<Footer />
</Router>
);
}
export default App;
BUT the trouble was on my Nav (a child component) module,
I had to 'import { withRouter } from 'react-router-dom';'
and then force an export with:
export default withRouter(Nav);
ex:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Nav extends React.Component {
render() {
return (
<div>
<label htmlFor='back'></label>
<button id='back' onClick={ () => this.props.history.goBack() }>Back</button>
<label htmlFor='logOut'></label>
<button id='logOut' ><a href='./'>Log-Out</a>
</button>
</div>
);
}
}
export default withRouter(Nav);
in summary, withRouter was created because of a known issue in React where in certain scenarios when inheritance from a router is refused, a forced export is necessary.
You can use history.goBack() in functional component. Just like this.
import { useHistory } from 'react-router';
const component = () => {
const history = useHistory();
return (
<button onClick={() => history.goBack()}>Previous</button>
)
}

Routing in Chrome Extension written in React

I want 2 pages in my Chrome extension. For example: first(default) page with list of users and second with actions for this user.
I want to display second page by clicking on user(ClickableListItem in my case). I use React and React Router. Here the component in which I have:
class Resents extends React.Component {
constructor(props) {
super(props);
this.handleOnClick = this.handleOnClick.bind(this);
}
handleOnClick() {
console.log('navigate to next page');
const path = '/description-view';
browserHistory.push(path);
}
render() {
const ClickableListItem = clickableEnhance(ListItem);
return (
<div>
<List>
<ClickableListItem
primaryText="Donald Trump"
leftAvatar={<Avatar src="img/some-guy.jpg" />}
rightIcon={<ImageNavigateNext />}
onClick={this.handleOnClick}
/>
// some code missed for simplicity
</List>
</div>
);
}
}
I also tried to wrap ClickableListItem into Link component(from react-router) but it does nothing.
Maybe the thing is that Chrome Extensions haven`t their browserHistory... But I don`t see any errors in console...
What can I do for routing with React?
I know this post is old. Nevertheless, I'll leave my answer here just in case somebody still looking for it and want a quick answer to fix their existing router.
In my case, I get away with just switching from BrowserRouter to MemoryRouter. It works like charm without a need of additional memory package!
import { MemoryRouter as Router } from 'react-router-dom';
ReactDOM.render(
<React.StrictMode>
<Router>
<OptionsComponent />
</Router>
</React.StrictMode>,
document.querySelector('#root')
);
You can try other methods, that suits for you in the ReactRouter Documentation
While you wouldn't want to use the browser (or hash) history for your extension, you could use a memory history. A memory history replicates the browser history, but maintains its own history stack.
import { createMemoryHistory } from 'history'
const history = createMemoryHistory()
For an extension with only two pages, using React Router is overkill. It would be simpler to maintain a value in state describing which "page" to render and use a switch or if/else statements to only render the correct page component.
render() {
let page = null
switch (this.state.page) {
case 'home':
page = <Home />
break
case 'user':
page = <User />
break
}
return page
}
I solved this problem by using single routes instead of nested. The problem was in another place...
Also, I created an issue: https://github.com/ReactTraining/react-router/issues/4309
This is a very lightweight solution I just found. I just tried it - simple and performant: react-chrome-extension-router
I just had to use createMemoryHistory instead of createBrowserHistory:
import React from "react";
import ReactDOM from "react-dom";
import { Router, Switch, Route, Link } from "react-router-dom";
import { createMemoryHistory } from "history";
import Page1 from "./Page1";
import Page2 from "./Page2";
const history = createMemoryHistory();
const App: React.FC<{}> = () => {
return (
<Router history={history}>
<Switch>
<Route exact path="/">
<Page1 />
</Route>
<Route path="/page2">
<Page2 />
</Route>
</Switch>
</Router>
);
};
const root = document.createElement("div");
document.body.appendChild(root);
ReactDOM.render(<App />, root);
import React from "react";
import { useHistory } from "react-router-dom";
const Page1 = () => {
const history = useHistory();
return (
<button onClick={() => history.push("/page2")}>Navigate to Page 2</button>
);
};
export default Page1;
A modern lightweight option has presented itself with the package wouter.
You can create a custom hook to change route based on the hash.
see wouter docs.
import { useState, useEffect } from "react";
import { Router, Route } from "wouter";
// returns the current hash location in a normalized form
// (excluding the leading '#' symbol)
const currentLocation = () => {
return window.location.hash.replace(/^#/, "") || "/";
};
const navigate = (to) => (window.location.hash = to);
const useHashLocation = () => {
const [loc, setLoc] = useState(currentLocation());
useEffect(() => {
// this function is called whenever the hash changes
const handler = () => setLoc(currentLocation());
// subscribe to hash changes
window.addEventListener("hashchange", handler);
return () => window.removeEventListener("hashchange", handler);
}, []);
return [loc, navigate];
};
const App = () => (
<Router hook={useHashLocation}>
<Route path="/about" component={About} />
...
</Router>
);

Categories