Search code examples
reactjstypescriptreact-hooksreact-routerreact-router-dom

Getting Module '"react-router-dom"' has no exported member 'RouteComponentProps error when migrating to react-router v6


We are having legacy class based code for which we are trying to migrate to latest react router that is version 6. During migration we are getting this error:

Module '"react-router-dom"' has no exported member 'RouteComponentProps'

We have added withRouter wrapper to add the legacy support.

import { useLocation, useNavigate, useParams } from "react-router-dom";

function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let location = useLocation();
    let navigate = useNavigate();
    let params = useParams();
    return <Component {...props} router={{ location, navigate, params }} />;
  }

  return ComponentWithRouterProp;
}

export default withRouter;

Our class based component looks like this:

import { Route, RouteComponentProps, Routes } from "react-router-dom";

class App extends React.Component<{} & RouteComponentProps<{}>, IState> {
  constructor(props: RouteComponentProps<{}>) {
    super(props);
    this.state = {
       prop1: null
    };
  }
   
  componentDidMount() {
    if (this.props.location.pathname === "/test") {
      window.location.reload();
    }
  };

   
  render() {
     return (
          <Routes>
               <Route path="/test" element={<Test />} />
          </Routes>
     )
  }
 }
 
export default withRouter(App);

How do I fix this issue?


Solution

  • Issues

    • react-router-dom@6 doesn't export any RouteComponentProps type. In fact, RRD6 completely did away with route props, the former location, navigate, and params values are now accessed using useLocation, useNavigate, and useParams respectively.
    • The withRouter Higher Order Component isn't injecting "route params", it is injecting a single prop named router that has the "route params".

    Solution

    In most cases the answer to "How do I fix this issue?" is to convert the Class components to Function components and use the hooks directly. For the sake of addressing your issue with legacy code though you'll have to type the "route props" manually yourself.

    Example:

    import {
      Location,
      NavigateFunction,
      Params,
      useLocation,
      useNavigate,
      useParams,
    } from "react-router-dom";
    
    export interface RouterProps {
      location: Location;
      navigate: NavigateFunction;
      params: Params;
    }
    
    export interface WithRouter {
      router: RouterProps;
    }
    
    function withRouter(Component: any) {
      function ComponentWithRouterProp(props: any) {
        const location = useLocation();
        const navigate = useNavigate();
        const params = useParams();
        return <Component {...props} router={{ location, navigate, params }} />;
      }
    
      return ComponentWithRouterProp;
    }
    
    export default withRouter;
    
    interface IState {
      prop1: .....
    }
    interface IProps extends WithRouter {
      .....
    }
    
    class App extends React.Component<IProps, IState> {
      constructor(props: IProps) {
        super(props);
        this.state = {
          prop1: null
        };
      }
    
      componentDidMount() {
        if (this.props.router.location.pathname === "/test") {
          // ...
        }
      }
    
      render() {
        return (
          <Routes>
            <Route path="/test" element={<Test />} />
          </Routes>
        );
      }
    }
    
    export default withRouter(App);
    

    Note: React Class components have been effectively deprecated since February 2019 when React hooks were released. It's now 2024, you should really try to write modern React using Function components. This is especially true when working with newer versions of libraries like react-router-dom that exclusively use React hooks.