5

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>
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Arijit Datta
  • 101
  • 1
  • 2
  • 10
  • 1
    It looks like you are using a class component here, can you edit to include a more complete/comprehensive component code example? This component and anything between it and where you are rendering your router. https://stackoverflow.com/help/minimal-reproducible-example – Drew Reese Mar 16 '22 at 16:52

3 Answers3

11

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);
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • How to do this in typescript? "const { navigate } = this.props;" does not work in typescript. Is there a solution that is not so hacky? – nagylzs Oct 06 '22 at 11:10
  • @nagylzs Something like this? https://codesandbox.io/s/navigate-using-react-router-dom-v6-after-performing-user-action-97djhs?file=/src/App.tsx – Drew Reese Oct 07 '22 at 05:57
  • Yes, that works thanks. Meanwhile I found an even better solution here https://stackoverflow.com/questions/69871987/react-router-v6-navigate-outside-of-components - what I really needed is a global navigate function that can be called from anywhere, including callbacks and async functions. React Router DOM is a bit overcomplicated from this perspective - I believe that the "navigate to a given url" should be a primitive built-in function of ANY library that implements routing. It is insane that you have to write HOCs and custom routers, just to be able to jump to a location... – nagylzs Oct 07 '22 at 07:06
  • @nagylzs Another of my answers there. Yeah, that is a good solution. If you happen to already be using Redux then I'd *highly* recommend checking out [redux-first-history](https://github.com/salvoravida/redux-first-history) which accomplishes similarly as my answer you linked ***but*** you can simply dispatch Redux actions to effect navigate changes. The `withRouter` HOC solution solves the different problem of needing to access router objects in Class components that you can now only access from React hooks. – Drew Reese Oct 07 '22 at 07:10
3

Routes must be contained into a Router (Usually) BrowserRouter, so, you should put them all inside of that component, something like this:

<BrowserRouter>
    <div className="App">
      <Box data-testid="app-container">
        <Routes>
          <Route path={"/"} element={<Home />} />
          <Route path={"/edit"} element={<edit/>} />
          <Route path={"/whatever"} element={<whatever/>} />
        </Routes>
      </Box>
    </div>
</BrowserRouter>

regarding to the navigate, in react-router-dom v6 you must use the hook useNavigate() and it works as:

  const navigate = useNavigate();

   <Button
       text={ES.common.back}
       onClick={() => navigate("/")}
   ></Button>

You'll have to import

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

Here's some documentation that you may find it helpful

Sergio18rg
  • 87
  • 1
  • 8
  • Sorry it did not help. if you look at the code { this.addPhoto(addedPost) }} >. I want to navigate to the "/" path immediately after executing this.addPhoto(addedPost).. with something like history.push("/") – Arijit Datta Mar 16 '22 at 16:28
  • What is the version of `react-router-dom` ? – semperlabs Mar 16 '22 at 16:55
  • I am using v 6.22 – Arijit Datta Mar 16 '22 at 18:39
  • This results in the following error: 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 react-hooks/rules-of-hooks – nagylzs Oct 06 '22 at 11:09
0

You can return something from the action and based on what you return, you can navigate wherever you want (even dynamically, for example navigate(-1))

A simple useEffect demonstrating that:

const actionData = useActionData()
React.useEffect(() => {
  if (actionData && actionData === 'navigate-up') {
    navigate(-1);
  } else if (actionData && actionData == 'navgiate-to-profile') {
    navigate('/profile');
  }
}, [actionData]);

Here's the complete StackBlitz example: https://stackblitz.com/edit/stackblitz-starters-dspvkg

Aakash
  • 21,375
  • 7
  • 100
  • 81