Skip to content
This repository was archived by the owner on Dec 15, 2018. It is now read-only.
This repository was archived by the owner on Dec 15, 2018. It is now read-only.

What are the performance implications of rendering the entire tree on location change? #182

@faceyspacey

Description

@faceyspacey

According to this line:
https://github.com/FormidableLabs/redux-little-router/blob/master/src/components/provider.js#L65

export const RouterProvider = connect(state => ({
  router: state.router
}))(RouterProviderImpl);

export default ({ store }: ProvideRouterArgs) =>
  (ComposedComponent: ReactClass<*>) => (props: Object) =>
    <RouterProvider store={store}>
      <ComposedComponent {...props} />
    </RouterProvider>;

the <Provider /> component renders the entire component tree every time the location state changes.

I see how it's efficient in that all your <Fragment /> components have no listeners, and as they are re-rendered, they simply grab router state from context:

https://github.com/FormidableLabs/redux-little-router/blob/master/src/components/fragment.js#L51

render() {
      const { children, forRoute, ...rest } = this.props;
      const { router, parentRoute, parentId } = this.context;
      const { store } = router;

      const location = store.getState().router;
     // ..

But overall this is highly inefficient. Your entire tree is rendering (even if just the virtual DOM) until a shouldComponentUpdate returns false (if you even have one).

The way Redux works is it subscribes components at the component-level:
https://github.com/reactjs/react-redux/blob/4d302257e3b361731f44b1f546e547ed578c8eec/src/components/connectAdvanced.js#L218

Thereby giving you the opportunity to isolate updates to the smallest relevant leaf nodes/components.

Perhaps this is the only way, given there's no other way to know where all the <Fragment /> components will be. I see that's how React Router works as well. I use Redux directly, so I'm less familiar with path-aware route components.

Overall it seems that Redux's "static" subscriptions created and defined by connect are a clear performance winner. Essentially you trade a bit more upfront work from the user to tell Redux which components need to [possibly] re-render, rather than re-render the whole tree. In addition, it's only "possibly," in which case only the comparisons are performed.

I mean, am I missing something, we're talking about renders of the ENTIRE tree vs. comparisons (which you have plenty of capability to optimize in userland) and possibly no render at all in plenty of cases.

I personally use route-aware actions to determine state, and let well-named state handle the rest. Basically I'm on the "switch" side of the "flexibility continuum" described here:

https://github.com/FormidableLabs/redux-little-router#fragment

I also struggle to see the value in nested routes. I always have with regards to React Router. From my perspective, it's added complexity having to carry around path segments throughout your components. It seems to be pretending to solve a problem--because it's really not that hard to toggle on a navbar or a sidebar for a given section of the site in response to state. Or return a component from a map whose key matches some state (or the token "switch").

And this whole example never happens in the wild:

/home/bio/dat-boi:

<Fragment forRoute='/home'>
  <div>
    <h1>Home</h1>
    <Fragment forRoute='/bio'>
      <div>
        <h2>Bios</h2>
        <Fragment forRoute='/dat-boi'>
          <div>
            <h3>Dat Boi</h3>
            <p>Something something whaddup</p>
          </div>
        </Fragment>
      </div>
    </Fragment>
  </div>
</Fragment>

There's no /home/bio/ URL that just shows the h2 "Bios"? Who's site actually does that? That URL is usually unreachable. It always comes paired with a user slug. I mean I understand, a better example would be where the 3 segments are: /home/posts/:slug, and with the slug you show a post profile and without it, you show a posts list. But is this really such a complex problem it's worth all our components being aware of paths, not to mention all the additional nesting?

paths are also subject to change, whereas it's your job to use well-named variables (i.e. state keys) that will stick around for longer.

Unlike React Router, we have the benefit of state at our disposal. So we have another mechanism to control what is visible. It seems once you have state, you no longer need <Route /> or <Fragment /> components. Route components essentially seem to be a carry-over from a bygone era.

I've always felt that React Router and it's "everything is a component" concept is a solution looking for a problem. The problem just doesn't exist, at least not with global reactive state at your disposal. If it did, a better example than that would be in the readme right here.


That all said, what I love about Redux Little Router is how it bi-directionally dispatches route-aware actions as the result of address bar changes and changes the address bar as the result of actions. With path params being extracted and passed to your reducers, that's all the power you need. Connect the components you need to be aware of such state, and done.

You have to think, if we didn't have URLs to deal with, is littering our component tree with paths what we would do? It seems to add more complexity and costs far more in terms of performance.

Am I missing something? Is this just for people that are hooked to the React Router "everything is a component" interface, but want it predictably driven by Redux state? Are most people using these <Fragment /> components, or are they using their own state derived from route-aware actions? My guess is everyone's following the React Router interface--because that's just what we do in Reactlandia. Please enlighten me though. Am I'm missing something that everyone else sees?

The way redux-first-router does this by the way is simply allowing users to use react-redux's "sideways data-loading" strategy (which is extremely efficient) to make their own routing components using location state in redux. RFR has no concept of <Route /> or <Fragment /> components. Instead it has better formatted action types with the name of the route, i.e. instead of the equivalent of LOCATION_CHANGED as the type for every call to history.push. Essentially making route-aware components comes down to using Redux state like normal. Most of the time in your connected components, instead of using the route type as state, you use derived state in the form of the state contained in other reducers, since it's now extremely easy for reducers to listen and switch over route changes.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions