0

I am updating a React with redux app to work with react-router-dom v6.

In the application, I have a helper file used for browser history:

_helpers.history.ts

  import { createBrowserHistory } from 'history';

  export const history = createBrowserHistory();

I used this in my user.actions.js file to navigate users to new views after login, registration, etc.

user.actions.js

import { history } from '../_helpers';

export const userActions = {
    login,
    logout,
    register,
    getAll,
    delete: _delete
};

function login(username, password, from) {
    return dispatch => {
        dispatch(request({ username }));

        userService.login(username, password)
            .then(
                user => { 
                    dispatch(success(user));
                    history.push('/dashboard');
                },
                error => {
                    dispatch(failure(error.toString()));
                    dispatch(alertActions.error(error.toString()));
                }
            );
    };

I replaced createBrowserHistory with useNavigate from react-router-dom:

history.js (updated)

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

export const history = useNavigate();

and in user.actions.js, history.push('/dashboard'); was replaced with navigate(dashboard)

However, trying to do this produces the following error:

ERROR in [eslint] 
src/_helpers/history.js
  Line 3:24:  React Hook "useNavigate" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function

I also use the _helpers.history.js in my App.js file: APP.JS

import { history } from "./_helpers";

function App() {
  return (
    <>
      <Routes history={history}>
        {/* Public Pages */}
        <Route exact path="/" element={<HomePage />} />

I've looked at a couple of S/O answers but they are all related to applications using functional components. What would be the correct method to update the helper file in this case?

Noble Polygon
  • 796
  • 11
  • 36

1 Answers1

-1

useNavigate can only be called inside the component, and it is called within the context of the router. I think what you want is to call history externally, which is not supported in react-router v6. But it can be achieved by customizing Router. like this:

// customRouter 
import { Router } from 'react-router-dom';
import { useRef, useState, useLayoutEffect } from 'react';
import { createBrowserHistory } from 'history';

export const history = createBrowserHistory();

export function CustomRouter({ basename, children }) {
  const historyRef = useRef(customHistory);
  if (historyRef.current == null) {
    historyRef.current = customHistory;
  }
  const myHistory = historyRef.current;
  const [state, setState] = useState({
    action: myHistory.action,
    location: myHistory.location,
  });

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

  return (
    <Router
      basename={basename}
      // eslint-disable-next-line react/no-children-prop
      children={children}
      location={state.location}
      navigationType={state.action}
      navigator={myHistory}
    />
  );
}


Then wrap your component with CustomRouter. like this:

const App = () => {
  return (
    <CustomRouter>
      // ... your routes here
    </CustomRouter>
  )
}

Then you can call history anywhere.

Sky Clong
  • 141
  • 1
  • 8