| stage | accepted | ||
|---|---|---|---|
| start-date | 2023-09-28 00:00:00 UTC | ||
| release-date | |||
| release-versions | |||
| teams |
|
||
| prs |
|
||
| project-link | |||
| suite |
Add a context API to allow sharing state with all descendants of a component without prop-drilling.
Ember provides developers with great APIs for sharing state and props. Services allow us to set global, application-wide state. Contextual components allow us to expose components pre-filled with data.
However, the available solutions aren't enough for situations where we want to access local state in deeply nested component trees. Services are global, but what if we want to render two component trees side by side, and have each of them expose a different "context state" to all descendants?
Similarly, while yielding does give us the ability to pass components or state around, we'd still have to explicitly add arguments to all components rendered within a section of the app.
Alternatively, we can also "prop-drill" - pass certain arguments through to all components, sometimes just so that one deeply nested component can access the information.
This is where a context API would greatly improve the developer experience. Some examples of where context might be better than existing solutions.
-
A design system
In a component library, a
Cardcomponent might accept abackgroundColorargument. The same component could then also expose this background color in its context state, making it available to all components nested inside it - no matter how deep.Other library components, like buttons or text components, could then check what background color they're rendered on, and adjust their own styles accordingly, to make sure the text and background color contrasts are accessible, or just to make sure the button background matches the card background ("danger"-style buttons in "danger"-styled cards). These components could adjust their color no matter how deeply nested they are inside the card, without us having to explicitly pass the background color through multiple layers of components.
-
Chart and graphical components
In these scenarios there might be multiple layers being rendered, where yielding or passing certain arguments can become cumbersome. A
Graphcomponent could render anAxis, which would renderTicks andTickLabels. The deeply-nested components will need access to the rootGraph's config to modify what and how they render.While passing the config through many layers is an option, context could make this sort of code arguably easier to read, write and maintain.
The feature has also been requested in this past, for example:
The details would still need to be worked out. The Glimmer VM already does a lot of the tree-tracking background work that would be necessary to pass context state through component layers.
For example:
-
This class is used by Ember Inspector, and it already tracks the hierarchy of components. We could do something similar to pass context state through component layers as they render.
-
When elements render, the Glimmer VM builds parent/child associations in the destroyable module. Again, this is the sort of hierarchy tracking that would be needed for a context API.
A very simple exploration into a DebugRenderTree-like context solution can be
found at https://github.com/customerio/glimmer-vm/tree/provide-consume-context.
This introduces a new class, which keeps track of "context provider" components,
and exposes their state to any descendant components.
The final implementation of context in Ember might be by using special decorators, like:
@provide('my-context-name')
get value() {
return 'my state';
}which would register the property to be exposed as context, and which could be consumed in any nested components via:
@consume('my-context-name') contextState;Other popular frameworks already include context APIs:
There are also Ember addons to provide similar functionality in Ember:
-
ember-context This addon introduces a context API that works quite well, but it doesn't actually pass context through parent-child relationships in the render tree, which makes it less robust and comes with caveats.
-
ember-contextual-services This addon provides locally scoped services for routes only (not components).