javascriptreactjsurl-routingreactjs-flux

React way to route without re-rendering


So I have a simple React/Flux app using Backbone's router. I have a case where the user creates an object, and the path updates from /object/new to /object/:id. However, there is no need to re-render the page, because the component is the same, and, due to the associated store updating after the ajax-create call returns, it updates itself.

Currently, I have just patched the router to expose a method that just updates the url, and doesn't actually hit the route-specific method. This feels hacky, and doesn't really address cases where some components (ie, a widget) need to be added/removed (at least it removes the responsibility of knowing which components need to be rendered out of the router), but the primary UI does not need to be re-rendered.

So this leaves me with three questions:

  1. What is the React way for handling url changes that don't require changing components?
  2. What about url changes that only add/change certain components?
  3. Should stores be responsible for initiating routing events?

Solution

  • One of the main value propositions of React is that re-rendering is very very cheap.

    This means that you can rerender excessively without negative effects. This is a complete 180 from Backbone, where rendering is very expensive, which leads to logic you are seeking, namely how to avoid renders.

    Under the hood, React does this check for you by diffing the Virtual DOM with the DOM. In other words: When you use the exposed render function in React, you don't really render the DOM, rather, you just describe the new state of the DOM with Javascript.

    In practice, this means that if you don't compute many values, you can constantly rerender at 60 frames per second without any optimization steps.

    This gives you the freedom to fully "re-render", even if only very few things on your app actually change.

    So my advice is to actually not try anything to prevent React to rerender the whole page, even if nothing changes. This kind of logic will add complexity, and you can avoid this complexity at no cost by unconditionally rerendering on a route change. This makes sense from a conceptional point of view too, since the route is nothing but global app state.

    The freedom to be able to do this is one of the main reasons that makes React awesome.

    It is a classic case of "premature optimization is the root of all evil".

    For example: I sometimes globally rerender the whole DOM hierarchy on mouseMove events, and there is no performance impact observable.

    As a general rule, think of a rerender as a zero-cost operation. Now you might have some expensive operations in your React components going. If this is the case, you can use the lifecycle methods of React to do these on demand. Especially have a look at shouldComponentUpdate, componentWillReceiveProps and componentWillUpdate .

    If you are using Flux and you adhere to the immutability paradigm, you can make very cheap referencial equality checks of state and props to do work on demand. With this, you can improve performance.

    With the shouldComponentUpdate method, you can prevent a render call if it requires too much computational power. However, I would only do this if it yields in an improved performance because of an expensive operation you implemented yourself.

    In your case, I would inject the route state in the root component, inject them as props into the children of the root and implement shouldComponentUpdate on them to prevent a render.