Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Routes rerender if I use a map instead of static components

I have 2 routes in my app, /validations (which is a list) and /validations/:id (which is the same list focused on a particular item).

I initially wrote my routes statically and everything works (as expected):

<Route path="/validations/:id" component={Validations} />
<Route path="/validations" component={Validations} />

However, because I wanted to do some refactoring, I put the routes in an array and used a .map to render them. But with this, when I click on an item, the whole page rerender and I lose the scroll of the list (this is a bad UX for the user because he loses where he was in the list):

const routes = [
  {
    path: '/validations/:id',
    component: Validations,
  },
  {
    path: '/validations',
    component: Validations,
  },
]

// ...and in the JSX...

{routes.map(({ path, component }) => (
  <Route
    key={path}
    path={path}
    component={component}
  />
))}

Even stranger, if I remove the key prop of the Route I get back the initial behaviour (but I have a warning by React).

Did I write something wrong? Is it a react-router bug? A React one?

How can I keep the scroll of the list with my route mapping?

Here is a reproduction: https://codesandbox.io/s/vm71x2k46l

like image 630
Kerumen Avatar asked Aug 08 '18 09:08

Kerumen


1 Answers

I knew something was fishy because I've done this many times and I didn't get your results. So I noticed that you render your sidebar inside your route.

This is why it's re-rendering and setting everything to the top. Because you actually navigate to another route.

What you must do is set your Sidebar links outside the actual routes but inside the Router Component.

Like in this sandbox: https://codesandbox.io/s/p2r4pl2o3q

If you notice I changed the routes a bit:

const routes = [
  {
    path: "/",
    component: Index
  },
  {
    exact: true,
    path: "/validations",
    component: Validations
  },
  {
    path: "/validations/:id",
    component: Validations
  }
];

And then created the sidebar component and set it outside the routes

<BrowserRouter>
  <div>
    <Sidebar />
    <div style={{ marginLeft: 150 }}>{routeComponents}</div>
  </div>
</BrowserRouter>

Sidebar:

var items = Array.apply(null, Array(300)).map(function(x, i) {
  return i;
});

export default () => (
  <div
    style={{
      width: 150,
      height: "100vh",
      display: "flex",
      overflow: "auto",
      position: "absolute",
      flexDirection: "column"
    }}
  >
    {items.map((item, index) => (
      <Link to={`/validations/${index}`} key={index}>
        Item {index}
      </Link>
    ))}
  </div>
);

Now since first click the scroll doesn't reset.

PS: Please don't mind the styling decisions I was just trying to see something work.

like image 195
João Cunha Avatar answered Nov 12 '22 00:11

João Cunha