87

I use react-router-dom version 6 and when I use this.props.history.push('/UserDashboard') it does not work. I changed it to

const history = createBrowserHistory();
history.push('/UserDashboard')

but I still have a problem that when i would like to redirect to /UserDashboard just the link change and the page still the first one??

any help??**

        handleSubmit(event){
       
    
        event.preventDefault();
        const history = createBrowserHistory();
        axios({
          method: "POST", 
          url:"http://localhost:3001/users/login", 
          data:  this.state
        }).then((response)=>{
          console.log(response.data.user.admin)
          if (response.data.success === true && response.data.user.admin === false){
           
                  const history = createBrowserHistory();
                  history.push({
                   pathname:"/users",
                   state:{
                   Key : response.data.user }
     });
    
        
           
          }else if(response.statusCode === 401 ){
            alert("Invalid username or password");
           window.location.reload(false);
          }
        })
      }

my routes.js file:

    import React from 'react';
    import { Navigate } from 'react-router-dom';
    import DashboardLayout from './Pages/DashboardLayout';
    import AccountView from './Pages/views/account/AccountView';
    import CustomerListView from './Pages/views/customer/CustomerListView';
    import DashboardView from './Pages/views/reports/DashboardView';
    import ProductListView from './Pages/views/product/ProductListView';
    import SettingsView from './Pages/views/settings/SettingsView';
    import Home from './Pages/home';
    import About from './Pages/About';
    import Partners from './Pages/Partners';
    import Services from './Pages/services';
    import Login from './Pages/Login';
    import RD from './Pages/RD';
    import ContactUs from './Pages/contactus';
    import Apply from './Pages/apply';
    import PartnerShip from './Pages/partnership';
    import News from './Pages/News';
    const routes = [
     {
     path: 'users',
     element: <DashboardLayout />,
     children: [
      { path: 'account', element: <AccountView /> },
      { path: 'customers', element: <CustomerListView /> },
      { path: 'dashboard', element: <DashboardView /> },
      { path: 'products', element: <ProductListView /> },
      { path: 'settings', element: <SettingsView /> }
      ]
     },
    {
    path: '/',
    element: <Home />,
    },
    {
    path: 'about',
    element: <About />
    },
     {path: 'partners',
     element: <Partners />,
    
    },
    {
    path: 'services',
    element: <Services />,
    
    },
    {
    path: 'contactus',
    element: <ContactUs />,
    
    },
    {
    path: 'login',
    element: <Login />,
    
     },{
    path: 'RD',
    element: <RD />,
    
    },
    {
    path: 'apply',
    element: <Apply />,
    
     },
     {
    path: 'partnership',
    element: <PartnerShip />,
    
     },
     {
    path: 'News',
    element: <News />,
    
     }
    ];

    export default routes;
Big Smile
  • 1,073
  • 16
  • 29
zineb
  • 879
  • 1
  • 5
  • 6

8 Answers8

106

In react-router-dom v6, you need to use useNavigate rather than useHistory.

See example from https://reacttraining.com/blog/react-router-v6-pre/

import React from 'react';
import { useNavigate } from 'react-router-dom';

function App() {
  let navigate = useNavigate();
  let [error, setError] = React.useState(null);

  async function handleSubmit(event) {
    event.preventDefault();
    let result = await submitForm(event.target);
    if (result.error) {
      setError(result.error);
    } else {
      navigate('success');
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      // ...
    </form>
  );
}
Luke Phillips
  • 2,252
  • 1
  • 10
  • 15
  • 17
    Problem is, how do we navigate from outside components? eg. when our logic is within `actions`? – Eduard Nov 20 '21 at 21:13
  • @Eduard have a look to my answer. This should solve your problem. – Poyoman Nov 24 '21 at 12:22
  • 1
    the docs: [Use useNavigate instead of useHistory](https://reactrouter.com/docs/en/v6/upgrading/v5#use-usenavigate-instead-of-usehistory) – Kevin Dec 20 '21 at 08:54
  • just for side note you can tell router to push the url or redirect the url just like useHistory. to do that you can use replace object (and it will replaces to your url ) like this: `navigate('success' , {replace: true}); ` – scripty dude Feb 22 '22 at 09:29
20

We all know there is no longer { useHistory } kind of thing in react-router-dom v6. There is better a way to do a work of useHistory.

First import useNavigate ...

import { useNavigate } from 'react-router-dom';

then just do this after importing

function Test() {
    const history = useNavigate();

    function handleSubmit(e) {
        e.preventDefault();

        history('/home');
    }

    return (
        <form onSubmit={handleSubmit}>
            <button>Subimt</button>
        </form>
    )
}
Suraj Rao
  • 28,850
  • 10
  • 94
  • 99
Daredevil
  • 209
  • 1
  • 2
  • 1
    Here you have just used history as the variable name, which actually does not make it hold the browser history object, this would work similarly even if you use any valid variable name instead of history – Subham Saha Mar 27 '22 at 09:57
18

Based on react-router-dom source code, you may do something like this :

import { Router } from 'react-router-dom';

const CustomRouter = ({
  basename,
  children,
  history,
}) => {
  const [state, setState] = React.useState({
    action: history.action,
    location: history.location,
  });

  React.useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      basename={basename}
      children={children}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
};

Then make your history come from outside :

import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

<CustomRouter history={history}>
 ...
</CustomRouter>
Poyoman
  • 1,218
  • 15
  • 23
  • Hey @poyoman, how would you achieve the same thing with a class-based component. I don't use functional components. – Fonty Dec 06 '21 at 03:35
  • @Fonty Just put my functional component inside the "render" method of your class component :) – Poyoman Dec 06 '21 at 09:05
  • 1
    Thanks @poyoman, i'll give it a go. Much better to use the most up to date version. – Fonty Dec 15 '21 at 04:21
  • Im using typescript, and there are no action, location and listen inside Navigator, there only 4 methods - createHref, go, push and replace – gavr Jan 25 '22 at 23:12
4

Reactjs v6 has come with useNavigate instead of useHistory.

=> firstly, you have to import it like this: import {useNavigate} from 'react-router-dom'.

=> then you only can you use it under a react functional component like this:

const navigate = useNavigate();

=> And then which route you want to navigate, just put that route name like this:

navigate("/about");

example: if you want to navigate to the about page after clicking a button.Then you should put

navigate("/about") under this onClick event:

<button onClick = {()=>navigate("/about")}>go to about page

thanks.

Nishant Kumar
  • 5,866
  • 17
  • 67
  • 95
4

Typescript CustomBrowserRouter Based on @Poyoman's answer:

Create CustomBrowserRouter component:

import React from "react";
import { BrowserHistory, Action, Location } from "history";
import { Router } from "react-router-dom"

interface CustomRouterProps {
    basename?: string,
    children?: React.ReactNode,
    history: BrowserHistory
}

interface CustomRouterState {
    action: Action,
    location: Location
}

export default class CustomBrowserRouter extends React.Component<CustomRouterProps, CustomRouterState> {
    constructor(props: CustomRouterProps) {
        super(props);
        this.state = { 
            action: props.history.action,
            location: props.history.location
        };

        React.useLayoutEffect(() => props.history.listen(this.setState), [props.history]);
    }

    render() {
        return (
            <Router
                basename={this.props.basename}
                children={this.props.children}
                location={this.state.location}
                navigationType={this.state.action}
                navigator={this.props.history}
            />
        );
    }
}

Use CustomBrowserRouter:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createBrowserHistory } from "history";
import CustomBrowserRouter from './CustomRouter/CustomBrowserRouter';

let history = createBrowserHistory();

ReactDOM.render(
  <React.StrictMode>
    <CustomBrowserRouter history={history}>
      <App />
    </CustomBrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);
  • Your class is using hooks inside inherited object, which is not allowed in react [hook rules](https://reactjs.org/docs/hooks-rules.html). I used a function instead to make it work. – N0xB0DY May 22 '22 at 12:57
3

Another solution if you're still using class components in react v6+, is injecting the new navigate object as history. This gets around the pesky problem of not being able to use navigate() within a class component, though you should try to move away from class components in the future. I found myself in this predicament with a large codebase as I'm sure others still are.

import React, { Component } from "react";
import { useNavigate } from "react-router-dom";

class MyClass extends Component {
  handleClick(e) => {
    this.props.history('place-to-route');
  }
}
export default (props) => (
  <MyClass history={useNavigate()} />
);
Michael Brown
  • 1,421
  • 1
  • 20
  • 31
2

Couldn't quite get @Reid Nantes version going so converted it to a functional component and it works nicely

import React from "react";
import { BrowserHistory, Action, Location } from "history";
import { Router } from "react-router-dom";

interface CustomRouterProps {
    basename?: string;
    children?: React.ReactNode;
    history: BrowserHistory;
}

interface CustomRouterState {
    action: Action;
    location: Location;
}

export const CustomBrowserRouter: React.FC<CustomRouterProps> = (props: CustomRouterProps) => {
    const [state, setState] = React.useState<CustomRouterState>({
        action: props.history.action,
        location: props.history.location,
    });

    React.useLayoutEffect(() => props.history.listen(setState), [props.history]);
    return <Router basename={props.basename} children={props.children} location={state.location} navigationType={state.action} navigator={props.history} />;
};

SpeedOfSpin
  • 1,521
  • 17
  • 20
-2

You can use the solution described in

https://reactnavigation.org/docs/use-navigation/

class MyBackButton extends React.Component {
  render() {
    // Get it from props
    const { navigation } = this.props;
  }
}

// Wrap and export
export default function(props) {
  const navigation = useNavigation();

  return <MyBackButton {...props} navigation={navigation} />;
}
Brian Donovan
  • 8,072
  • 1
  • 25
  • 25