davidgilbertson/react-recollect

Top down rendering?

joelmoss opened this issue · 3 comments

So react-redux has a few performance optimisations built in, that I believe are important. The main one being that components are updated in a top down manner. So if parent and child components are subscribed to the same state, and that state changes, react-redux will make sure the parent component re-renders before the child.

How does react-recollect handle this?

This currently isn't handled explicitly. I will look into it.

I think it might already be optimal in most cases, but I'm lacking tests that explicitly assert this, so will at the very least add those before closing this.

Initial thoughts:

  • All updates happen via a simple setState() in the wrapper component, so updates are batched by React by default IF those updates originate from an event fired by a React component. E.g. React batches these two component updates. So I'm pretty sure "responding to user activity" updates are optimised.
    image

  • Updates originating outside a React component (network responses etc) aren't batched by React, so are a candidate for optimisation (this would save CPU time, not reduce DOM updates). Perhaps in this case I can check prevState in shouldComponentUpdate in collect.tsx to see if a component has already been updated with the next store. (That second render only happens because the store is shallow cloned before setting state.)
    image

  • Updates are fired in initial render order, and parent components start rendering before child components, so parent-first is the default. (I need to investigate if there's a case where a lower-level component could wind up before it's parent in the register of listeners).

  • I previously considered skipping component updates where an ancestor is flagged for an update too, but the possibility of an intermediate pure component means that a descendant/ancestor relationship between two components doesn't mean the lower-level one can be skipped. Of course, this doesn't mean rendering the parent first wouldn't be faster in the cases where it does trigger a re-render of the child.

  • First prize is letting React do the optimizations, so note to self: check out unstable_batchedUpdates exposed by ReactDOM.

Thanks for prompting me to dig into this @joelmoss.

Updating was optimal in most cases, but there were cases of wasted work, e.g.:

  • a parent and child component are both wrapped in collect
  • the parent passes props to the child
  • a store property used by both components is updated, triggering an update on both components
  • the change is such that the props passed from parent to child are also updated (so now, both the parent component and Recollect want to re-render the child)

This was causing a double-render of the child, but is now fixed ( in 5.2.2) using React's unstable_batchedUpdates to ensure all updated components get a single render.

image

The scenario is covered with this test: https://github.com/davidgilbertson/react-recollect/blob/master/tests/integration/renderBatching.test.tsx

The React folks have kindly confirmed that unstable_batchedUpdates isn't going to break outside of a semver change. facebook/react#18602.

Great stuff, thx 👌