hookrouter icon indicating copy to clipboard operation
hookrouter copied to clipboard

Children routes components remount every route change

Open silviodeligios opened this issue 5 years ago • 10 comments

I'm using the example in the documentation

import React, {useEffect} from 'react'
import {FocusStyleManager} from '@blueprintjs/core'
import {A, useRoutes} from 'hookrouter'

const AboutArea = () => {
  const routeResult = useRoutes({
    '/people': () => 'We are happy people',
    '/company': () => 'Our company is nice',
  })

  useEffect(() => {console.log('AboutArea mounted')}, [])

  return (
    <div className="about">
      <A href="people">About people</A>
      <A href="company">About our company</A>
      {routeResult}
    </div>
  )
}

const App = () => {
  const routeResult = useRoutes({
    '/': () => 'Home',
    '/about*': () => <AboutArea/>,
  })

  useEffect(() => {console.log('App mounted')}, [])

  return (
    <div>
      <A href="/about/people">Show about area</A>
      {routeResult || 404}
    </div>
  )
}
export default App

If you try to change route the AboutArea component will mount on every route change. This is a problem in the moment that I want to make api calls in the AboutArea component and share results with children. am I doing something wrong or is an issue?

silviodeligios avatar Aug 31 '19 14:08 silviodeligios

https://github.com/Paratron/hookrouter/issues/61#issuecomment-528069797

This has been raised before and should be seriously considered given the drawbacks

StallionV avatar Sep 04 '19 20:09 StallionV

Sorry, I had responded by mail a couple of days ago already - sadly my mail response appearently did not may its way here. But here is my original response:

Without having looked deeply into your code: a huge warning sign is that you are re-creating new route objects upon each render of your components.

Always declare your route objects as constants outside of your components. Its been written that way in the example as well - and there is even a note about this in the docs.

I am 90% sure this will make your problem go away.

Paratron avatar Sep 05 '19 10:09 Paratron

Hi Paratron, thanks for maintaining this project :-)

I'm having the same problem and it's not only due to re-creating new route objects.

There was a change in router.js (8f3a17868449515ea330d9d30a29561857f47845 - Do performance optimizations ). Here the resultFunc and resultProps were removed from

  • the assignment to the stackObj (line 301 + 302) in process()
  • construction of stackObj (line 343 + 345) in useRoutes()

As far as I can see this means that those are always null in process() now and the effect is that

  • funcsDiffer is always true
  • propsDiffer is always true
  • setUpdate(Date.now()); will always be called by the end of process() and that will re-mount components on every route change

Please have a look at this :-)

jmich avatar Sep 18 '19 07:09 jmich

Downgrading to 1.2.0 (which is before the mentioned change) solves the problem

jmich avatar Sep 18 '19 07:09 jmich

Thanks for pointing this out. I'll have a look.

Paratron avatar Sep 18 '19 08:09 Paratron

Having the same problem; downgrading to 1.2.0 also fixed it for me. I realize that in some cases performance hit is negligible, but when it comes to this like using CSS transitions for child components it becomes a deal-breaker issue.

clintharris avatar Nov 08 '19 00:11 clintharris

This bug bit me too late. I have routes in constants outside of function components. And nested routing produces re-rendering of the parent component when I switch to one of its sub-routes. And downgrading to 1.2.0 doesn't helps.

dvdvdmt avatar Jan 22 '20 08:01 dvdvdmt

Same problem here. Downgrading to 1.2.0 also fixed it for me. I love this library and would be nice to know if we are doing something wrong...

Here is a basic example of the problem in codesandbox.io If you navigate to "/about" page renders the current time and switching between "/people" and "company" remounts AboutPage. If you change the dependency version from 1.2.3 -> 1.2.0 the parent component is not re-mounted on route changed anymore.

JoshVazq avatar Feb 04 '20 15:02 JoshVazq

If downgrading to 1.2.0 doesn't help then see if some ancestor component is using usePath() without the active parameter set to false.

From the docs

The hook will automatically render the component again, if the path changes. If you don't need that, you can use the hook in passive mode: usePath(false) and it just returns the current path when the component renders and does not trigger renders upon path change.

https://github.com/Paratron/hookrouter/blob/master/src-docs/pages/en/04_other-features.md#using-the-uri-path

stefanbugge avatar Jun 18 '20 14:06 stefanbugge

I tried moving the routes objects as a constant outside of the class, and set usePath(false), but none of it fix the re-rendering problem with child route navigate.

Downgrading to 1.2.0 works.

AmethystLiang avatar Oct 25 '20 21:10 AmethystLiang