0

Expected: when clicking on the image from one component(PhotosList), it routes to another page with the specified url to view the other component(Image details)

Reality: what happens that when clicking on the image, both components are rendered on the same page but with the different url.

using import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

Here's the return of the functional component(PhotosList)

 return (
    <Router>
      <div className="layout">
        <Masonry gutter={"5 px"} columnsCount={3}>
          {photosList.map((photo: any) => (
            <Link to={"/details"}>
              <img src={photo.download_url} />
            </Link>
          ))}
        </Masonry>

        <button onClick={loadMore} className="btn-grad">
          {isLoading ? "Loading..." : "Load More"}
        </button>
        <Switch>
          <Route exact path={"/details"} component={ImageDetails}></Route>
        </Switch>
      </div>
    </Router>
  );

and the app file is

const App = () => {
  return (
    <div className="App">
      <header>Album</header>
      <PhotosList />
    </div>
  );
};

export default App;
3
  • Have you tried using NavLink instead of Link? Link is intended for redirecting the user outside of your app. NavLink is meant for all navigation within the app. Commented Aug 26, 2021 at 13:32
  • I just realized that whenever you redirect to '/details', the current component (which spawns the /details route) will be unmounted. Instead move this route inside of the root router. This should not be done this way. I prefer to have my router at the top level which is why i'd never get this kind of error. I have heard of route splitting before, i havent used it however. I am pretty sure it is not done this way though. Commented Aug 26, 2021 at 13:37
  • @DimitarDev I didn't find that difference between navlink and link in the documentation. I tried to put router at the root but nothing changes. Commented Aug 26, 2021 at 15:26

2 Answers 2

1
return (
      <div className="layout">
        <Masonry gutter={"5 px"} columnsCount={3}>
          {photosList.map((photo: any) => (
            <Link to={"/details"}>
              <img src={photo.download_url} />
            </Link>
          ))}
        </Masonry>

        <button onClick={loadMore} className="btn-grad">
          {isLoading ? "Loading..." : "Load More"}
        </button>
      </div>
  );

and in the app file

const App = () => {
  return (
   <Router>
    <div className="App">
      <header>Album</header>
      <PhotosList />
      <Switch>
          <Route exact path={"/photilist"} component={PhotosList}></Route>
          <Route exact path={"/details"} component={ImageDetails}></Route>
        </Switch>
    </div>
   </Router>
  );
};

export default App;
Sign up to request clarification or add additional context in comments.

Comments

0

From what I can gather, you have rendered the PhotoList component which then renders a list of photos each with a link to /details.

In the same component you set up the /details route.

Whenever you click the Link which redirects to /details, the PhotoList component will (should) unmount. Once this component is unmounted, so is the Route which sets up the component which should be rendered on /details route. So you are on /details but the router has no idea what component should be rendered because the following code:

<Switch>
  <Route exact path={"/details"} component={ImageDetails}></Route>
</Switch>

no longer exists.

To avoid this, don't put Routes this far down in your application. Routes should always be close to the top level (children/grandchildren of Router). This makes projects way easier to manage. I do have projects with upwards of 100 routes and I do split up my routes into seperate files (for example each module will have a ModuleRoutes.js file in which I set up the routes for that module), then you can create a RootRouter components which renders these seperate JS files (Which are really React components) and you render the RootRouter inside of your <BrowserRouter> / <Router>.

You can abstract this even higher by creating an array of objects which hold details about each route (route name, path, component for the route, function / data you want to pass down to the component, access control (true/false) defined as a logical expression of your choosing - i.e. user.isLoggedIn === true). By employing this abstraction you minimize the room for error because errors and bugs are much easier to spot when they are in this kind of form (object) as opposed to JSx which takes up more lines of code, has messy indentation and is not very clean in general.

1 Comment

Thank you a lot for your clear explanation!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.