154

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,103
  • 16
  • 30
zineb
  • 1,549
  • 2
  • 7
  • 6

16 Answers16

176

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
  • 3,372
  • 1
  • 11
  • 15
  • 33
    Problem is, how do we navigate from outside components? eg. when our logic is within `actions`? – Eduard Nov 20 '21 at 21:13
  • 1
    @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}); ` – Rman__ Feb 22 '22 at 09:29
  • let? why not const? – Pablo Recalde Jun 19 '23 at 16:09
36

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

// === Javascript version  === //

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}
    />
  );
};
// === typescript version === //
    
import * as React from "react";
import { BrowserHistory } from "history";
import { Router, Navigator } from "react-router-dom";

type Props = {
  basename?: string;
  children: React.ReactNode;
  history: BrowserHistory;
}

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

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

Then make your history come from outside :

import { createBrowserHistory } from 'history';

const history = createBrowserHistory();

<CustomRouter history={history}>
  ...
</CustomRouter>
Poyoman
  • 1,652
  • 1
  • 20
  • 28
  • 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
  • 1
    @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
23

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
  • 29,388
  • 11
  • 94
  • 103
Daredevil
  • 287
  • 2
  • 2
  • 8
    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
10

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,995
  • 19
  • 69
  • 95
6

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')
);
Reid Nantes
  • 61
  • 1
  • 4
  • 1
    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. – Adnan May 22 '22 at 12:57
  • Mistakenly upvoted this. As N0xB0DY said. You can't use hooks inside a class based component. – cdeutsch Jul 13 '22 at 19:43
6

user useNavigate for react-router-dom v6

import {useNavigate} from 'react-router-dom';
const navigate = useNavigate();
navigate('/home')
5

solution is

In v6, this app should be rewritten to use the navigate API. Most of the time this means changing useHistory to useNavigate and changing the history.push or history.replace callsite.

    // This is a React Router v5 app
import { useHistory } from "react-router-dom";
        
        function App() {
          let history = useHistory();
          function handleClick() {
            history.push("/home");
          }
          return (
            <div>
              <button onClick={handleClick}>go home</button>
            </div>
          );
        }

check out the article .... https://reactrouter.com/en/v6.3.0/upgrading/v5#use-usenavigate-instead-of-usehistory

// This is a React Router v6 app
import { useNavigate } from "react-router-dom";

function App() {
  let navigate = useNavigate();
  function handleClick() {
    navigate("/home");
  }
  return (
    <div>
      <button onClick={handleClick}>go home</button>
    </div>
  );
}

navigate push and navigate return is not required.

hungry coddie
  • 51
  • 1
  • 1
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,585
  • 1
  • 22
  • 36
3

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,611
  • 20
  • 21
2

Best to use a module:

let _navigate
export const navigate = (...args) => _navigate(...args)
export const useNavigateSync = () => {
   _navigate = useNavigate()
}

Run useNavigateSync within your top level component. Import navigate anywhere within your code.

  • Nice idea ! However using "useNavigate" aka a hook, outside a component is not react-friendly, i'm not sure it's a clean solution. – Poyoman Jun 22 '22 at 08:04
2

Referencing @poyoman solution, if you're looking for their implementation in TypeScript form you can try this:

import React, { FC, useLayoutEffect, useState } from 'react';
import { Router, RouterProps } from 'react-router-dom';
import { MemoryHistory } from 'history';

interface CustomRouterProps extends Omit<RouterProps, 'location' | 'navigator'> {
  history: MemoryHistory;
}

export const CustomRouter: FC<CustomRouterProps> = ({ basename, children, history }) => {
  const [state, setState] = useState<Pick<MemoryHistory, 'action' | 'location'>>({
    action: history.action,
    location: history.location,
  });

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

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

Uzair Ashraf
  • 1,171
  • 8
  • 20
0

Can be used from props in classes like this: Based on timdorr's answer on Github

Use the below prop in a class method:

this.props.navigate("/about")

with a route wrapper as described below:

// Routes in app.js
import MovieForm from "./components/movieForm";
import ElementWrapper from "./components/elementWrapper";

<Route path="/movies/new" element={<ElementWrapper routeElement={MovieForm} />} />

// i.e. specific to this question:
function getElement(route) {
    return <ElementWrapper routeElement={route} />
}
children: [
  { path: 'account', element: getElement(AccountView) },
  { path: 'customers', element: getElement(CustomerListView) },
  { path: 'dashboard', element: getElement(DashboardView) },
  { path: 'products', element: getElement(ProductListView) },
  { path: 'settings', element: getElement(SettingsView) }
  ]

// Wrapper in elementWrapper.jsx in /components
import React from "react";
import { useNavigate } from "react-router-dom";

const ElementWrapper = (props) => {
    const navigator = useNavigate();
    const Element = props.routeElement;

return <Element navigator={navigator} {...props} />;
};

export default ElementWrapper;
MTraveller
  • 31
  • 2
0
import {useNavigate} from 'react-router-dom'

class EmployeeComponent extends Component {
   constructor(props) {
      const { navigate } = this.props;
      this.saveEmployee= this.saveEmployee.bind(this);
   }

  saveEmployee=(e) => {
    e.preventDefault()
     this.props.navigate("/employees")
  }
}
export default function(props) {
    const navigate = useNavigate();
    return <EmployeeComponent {...props} navigate={navigate} />;
}
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 14 '22 at 15:58
0

maybe the question was forgotten but I found one nice solution. Use HOC and withRouter to create a parent element that will provide props with things as params, history... In withRouter element you must add all hooks that you would use in functional component. Example:

Create withRouter.js:


import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
 
const withRouter = WrappedComponent => props => {
  const params = useParams();
  const navigate=useNavigate();
  return (
    <WrappedComponent
      {...props}
      params={params}
      navigate={navigate}
    />
  );
};
 
export default withRouter;

Later on inside you component where you want to use:

MyClassComponent:


import withRouter from '../components/WithRouter';

...
this.props.navigate('/')
this.props.params
...

export default withRouter(connect(mapStateToProps,{fetchMarku})(EditMarke)); //EditMarke is the name of component
//If you are not using Redux just go:
export default withRouter(EditMarke);

I am using all of this and works fine. Links I combined:

react-router withRouter does not provide props in mapStateToProps?

https://www.cluemediator.com/how-to-access-url-parameters-in-the-class-component-using-react-router-v6

kapizza
  • 1
  • 1
0

I've made a way to solve this issue without custom router, so I can use navigate outside react component, and I can keep using the createBrowserRouter at the same time.

First create the setup method:

import { NavigateFunction, Outlet, useNavigate } from "react-router-dom";

interface MyHistory {
  navigate: NavigateFunction
}

export const myHistory: MyHistory = {} as MyHistory; 

export const SetupNavigation = () => {
  const navigate = useNavigate();
  useLayoutEffect(()=>{
    myHistory.navigate = navigate;
    // if you have any setup or loaders that depend on navigation, you can call them here, or after this is created.
  },[]);
  return <Outlet />;
}

then at the very first top Route inside your router, no matter what you use:

const route = createBrowserRouter(
  createRoutesFromElements(
    <Route
      path="/"
      handle={{
        crumb: () => <Link to="/app">App</Link>,
      }}
      // add the setup navigation here.
      element={<SetupNavigation />}
    >
      <Route
        index
        element={
          <StoresConsumer>
            {({ AccountStore: { isSignedIn } }) =>
              isSignedIn ? <Navigate to="app" /> : <Navigate to="signIn" />
            }
          </StoresConsumer>
        }
      />
    </Route>
  )

and now you can use it anywhere outside react components:

import {myHistory} from "./setupNavigation";

// don't destruct 'myHistory' object, 
// always use the reference so you use navigate after it's created.
const {navigate} = myHistory; // navigate is always undefined.

const login = (data: LoginData) => axios.post("/login",{data}).then(()=>{
   myHistory.navigate("/app/dashboard");
});

IMPORTANT NOTE

This has one issue. You can't use the myHistory.navigate before routes are created, obviously. So if you have any function to setup the app that needs navigation, you should call it inside SetupNavigation.

Also react router has redirect function which, according to docs, you can use with fetch, but I didn't figure out how it works yet.

Adnan
  • 186
  • 2
  • 8
-4

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,274
  • 1
  • 26
  • 25