Skip to content

[RFC] Idea: forceDeepUpdate() and forceDeepUpdateWithScope(scope) #7759

Closed
@sebmarkbage

Description

@sebmarkbage

Just going to put it out there for feedback...

Motivation

Subscription management comes with a cost and that eats into the wins of async rendering since it needs to be managed synchronously. Not just managing the direct subscriptions themselves but managing the dynamic dependency graph so that it can be invalidated.

Meanwhile, most of what subscriptions are used for is data that will never update. At least in our apps. It is a pure loss.

The use case is when you're connecting to third party systems that aren't as easily connected to the top level data tree.

Proposal

this.forceDeepUpdate();

Same use case as forceUpdate, if you are reading from global mutable state for some reason, you can use this to by-pass shouldComponentUpdate in an entire subtree. Basically rerender everything. When combined with Fiber this can be a low-priority update so it's not so bad for things that change a lot of things.

A good example would be changing the locale. Regardless if you read a global mutable locale (like AirBnB does) or a context locale (like Yahoo) does, this lets you change it when you need to. Without needing to manage subscriptions for all those cases when you don't need it.

this.forceDeepUpdateWithScope(scope);
class Foo extends React.Component {
  shouldComponentUpdateForScope(scope) {
    return scope.store === UserStore && scope.id === this.props.userID;
  }
  render() {
    ...
  }
}

forceDeepUpdateWithScope would traverse the subtree and only start rendering if shouldComponentUpdateForScope returns true for the arbitrary scope argument. This allows for a bit more of a targeted update with some convenience overhead.

Additionally, React would cache the pair of scope and components that responded. For some number of scopes back. If a new component gets mounted with a shouldComponentUpdateForScope we might check it against the cache to see if we need to add it to the cache.

Effectively this creates lazy subscriptions.

The use case is something like typing into an input field that then updates some global store which immediately displays in a completely different place on the page. The first character might be a bit slower but still with responsive levels and the subsequent characters are fast to update.

Caveat

The major downside of this proposal is that it relies on mutation. As we know, React doesn't really like mutation for many more reasons than just shouldComponentUpdate.

The effect in Fiber for example, is that any component that gets a higher priority update will start using the new value. Components that rely on mutable state effectively become up-prioritized which is not good. 1) It can temporarily show inconsistent data. 2) The point of making this kind of update lower priority is because it is likely to be large. Larger updates will stall the page if they take the same priority as higher priority updates. Thereby defeating the benefits of Fiber anyway.

I'd like to try to come up with a variant of this API that doesn't rely on mutation.

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