Search code examples
reactjsreact-routerreact-router-dom

Navigate using react-router-dom v6 after performing user action


I started learning react about 15 days back. The following code adds the post correctly but does not redirect to "/". I am using react-router-dom v6.

render(){
  return <div>
              <Routes>
                <Route exact path="/" element={
                      <div>
                      <Title title={'Arijit - Photowall'}/>   
                        <Photowall posts={this.state.posts} onRemovePhoto={this.removePhoto} /> 

                      </div>

      } >
                          </Route>
                    <Route path="/addPhotos" element={ 
                     
                    <AddPhoto onAddPhoto={(addedPost)=>{
          
                      this.addPhoto(addedPost)
                      
                      
                      }}
                      
                      >
                        <Navigate to="/" />
                      </AddPhoto>
                      
                     }/>
                  </Routes>
        </div>
}

Solution

  • In react-router-dom@6 the way to issue imperative navigation actions is to use the navigate function returned from the useNavigate hook. The code you've shared in the snippet is from a class component though, so you'll need to create a Higher Order Component to use the useNavigate hook and inject the navigate function as a prop.

    Example:

    import { useNavigate } from 'react-router-dom';
    
    const withNavigate = Component => props => {
      const navigate = useNavigate();
      return <Component {...props} navigate={navigate} />;
    };
    

    Decorate the component in your snippet with this withNavigate HOC.

    export withNavigate(MyComponent);
    

    Access the navigate function from props.

    render(){
      const { navigate } = this.props;
    
      return (
        <div>
          <Routes>
            <Route
              path="/"
              element={(
                <div>
                  <Title title={'Arijit - Photowall'}/>   
                  <Photowall posts={this.state.posts} onRemovePhoto={this.removePhoto} /> 
                </div>
              )}
            />
            <Route
              path="/addPhotos"
              element={( 
                <AddPhoto
                  onAddPhoto={(addedPost) => {
                    this.addPhoto(addedPost);
                    navigate("/");
                  }}
                />
              )}
            />
          </Routes>
        </div>
      );
    }
    

    Using Typescript

    interface WithRouter {
      location: ReturnType<typeof useLocation>;
      navigate: ReturnType<typeof useNavigate>;
      params: ReturnType<typeof useParams>;
    }
    
    const withRouter = <P extends {}>(Component: React.ComponentType<P>) => (
      props: Omit<P, keyof WithRouter>
    ) => {
      const location = useLocation();
      const navigate = useNavigate();
      const params = useParams();
    
      return <Component {...(props as P)} {...{ location, navigate, params }} />;
    };
    

    Example Usage:

    interface MyComponentProps {
     foo: string;
    }
    
    type MyComponentPropsWithRouter = MyComponentProps & WithRouter
    
    class MyComponent extends React.Component<MyComponentPropsWithRouter> {
      render() {
        const { foo, navigate, location, params } = this.props;
        const { bar } = params as { bar?: string };
    
        return (
          <>
            <h1>MyComponent: {location.pathname}</h1>
            <h2>Foo prop: {foo}</h2>
            <h2>Param?: {bar}</h2>
            <button type="button" onClick={() => navigate("/test")}>
              Navigate
            </button>
          </>
        );
      }
    }
    
    const MyDecoratedComponent = withRouter(MyComponent);