Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BUGFIX canary] Decouple route transition from view creation
This closes #9814 and closes #10304, which are examples of a class of problems caused by the way the router synchronously reaches into the view hierarchy to set outlet state. It is a significant refactor of the way the router communicates state to outlets. What motivates this change? - Simple examples like `{{#if something}}{{outlet}}{{/if}}` are incorrect under the current model. - Richer examples like block-helpers to enable animation also suffer. In general, the router cannot know when and whether a particular outlet is going to exist, and it shouldn't need to know. - The router maintains a bunch of view-related state that is actually redundant with the view hierarchy itself, leading to unnecessary complexity. - This eliminates the longstanding weirdness & confusion caused by the fact that we used to create new `View` instances and then throw them away if they looked similar enough to ones that were already rendered. That functionality is now covered by state diffing in the `OutletView`. - We reduce the API surface area between router and view layer in a way that should make it easier to experiment with swapping in compatible implementations of either. - As a bonus, this changes makes outlets work in an observer-free way that will make them easy to integrate with upcoming planned view layer optimizations. How does this work? - Rather than directly building and linking views, the router builds up an abstract summary of the render decisions that have been made by the current routes. - This state is cheap to recalculate as needed. It doesn't do any view creation. To avoid expensive observer creation & teardown, we just recreate the whole thing and use a `setState`-like mechanism to propagate the changes through the outlet hierarchy. This gives us optimal granularity of updates. - Actual view instantiation moves into the OutletView -- within the view layer where it belongs. Each outlet does a diff to see whether it should rerender itself or propagate inner changes down to its child outlets. - To bootstrap rendering, the router creates a single top-level outlet, after which all view creation is internal to the view layer. Does this break any existing semantics? - No, as far as I can tell. Could this get even better if we decided to deprecate some old semantics? - Yes. It would be better if users` `renderTemplate` implementations on `Route`s were required to be idempotent. Then we could eliminate a bunch of the remaining state from them. - Also, when we deprecate the `render` helper we can eliminate the remaining use of `_activeViews` state tracking on the router. That is the only remaining use for it.
- Loading branch information