useSubscription
and useMutableSource
1 tearing and deopt behavior.
The tree below represents a React application mounting. During mount, two components read from an external, mutable source. The first one (List
) reads version 1 of that data and the second one (Item
) reads version 2.
N/A.
N/A.
N/A.
Such a mutation during render should not occur during legacy mode (since legacy rendering is synchronous).
useSubscription
will not be able to detect the mutation during render, so the initial commit will tear. An update will be scheduled to fix the tearing, but not until a later frame2 (so the torn render will be temporarily visible).
useMutableSource
will detect the mutation during render and will throw to restart the render.
The tree below represents a React application updating previously mounted components. During the update, two components read from an external, mutable source. The first one (List
) reads version 2 of that data and the second one (Item
) reads version 3.
Every component subscribed to the source will be rendered and committed individually, potentially resulting in multiple separate paint/layout operations3. These operations won't be visible to the user, since they would all be synchronous, but it would be less efficient than if they were batched (and may also delay other, more important work).
Every component subscribed to the source will be batched together into a single render.
Every component subscribed to the source will be batched together into a single render.
Such a mutation during render should not occur during legacy mode (since legacy rendering is synchronous).
useSubscription
will not be able to detect the mutation during render, so the update will commit with tearing. A subsequent update will be scheduled to fix the tearing, but not until a later frame2 (so the torn render will be temporarily visible).
useMutableSource
will detect the mutation between renders. In many cases, it may be ale to re-use the previous value for the current render, but if that is not possible it will throw and restart the render.
The tree below represents a React application rendering a new subtree into an existing app. In a previous render, some components in the app (List
, Item
) read from version 3 of an external, mutable source. Sometime before this update the source changed, so during the update a new component (Item
) reads version 4.
Every component subscribed to the source will be rendered and committed individually, potentially resulting in multiple separate paint/layout operations3. These operations won't be visible to the user, since they would all be synchronous, but it would be less efficient than if they were batched (and may also delay other, more important work).
Every component subscribed to the source will be batched together into a single render.
Every component subscribed to the source will be batched together into a single render.
Note that in most cases, such a mutation should not occur in legacy mode (since a mutation in the source would typically schedule a synchronous update). However, because subscriptions are added in a passive effect2 it is possible for a mutation to happen before they are attached.
useSubscription
will not be able to detect the mutation between renders, so the update will commit with tearing. A subsequent update will be scheduled to re-render the previously mounted parts of the tree though.
useMutableSource
will detect the incompatible store versions and will throw to restart the render.
1 The useMutableSource
API does not currently exist but an RFC for it will be posted soon.
2 This is done to support edge cases in legacy mode (see codesandbox.io/s/k0yvr5970o).
3 This can be avoided if the subscription source uses the unstable_batchedUpdates
API to wrap its subscription triggering, although this is an unstable API so it's probably not suitable for a lot of cases.
Thanks for the good explanation 👍
There is a typo in Updating a tree part :)