Share a State Manager Across Components

To share a state manager instance across components, all consumer components must be children of a parent provider component that creates or acquires a reference to the shared state manager. In consumer components, use the fromContext function to retrieve a reference to the shared state manager.

A state manager instance is available to the component that creates it, and to all descendant components. Only the outer, enclosing component needs to create the state manager instance. Descendant consumer components must import the state manager definition, but instead of creating a state manager, they retrieve a reference to an existing state manager instance from their component context.

Consumers retrieve the closest state manager instance of the requested type, starting with themselves, and working upwards in their component hierarchy.

State manager instances are resolved when a component, whether provider or consumer, is connected to the DOM.

That is, the provider component:

  • Must have a reference to the state manager instance in one of its properties when it's connected to the DOM.
  • Must not change that reference after it's connected (the behavior is undefined if it does).

And the consumer component:

  • Does not receive a reference to the state manager instance until it's connected to the DOM.
  • Must not try to reference the state manager instance's value before the component is connected to the DOM. For example, in its constructor or any code that might be called from its constructor.

Diving a little deeper, all of the state manager context "magic" happens when the component is connected to the DOM. In the diagram in Lifecycle Hooks, for the provider component, resolving the state manager context happens in the "Parent inserted into DOM" box. For the consumer component, resolution happens in the "Child inserted into DOM" box.

This constraint means that the provider must set the state manager property during construction or initial property processing, and the consumer won't have its reference to the state manager until its connectedCallback runs.

This example is visually identical to the all-in-one example component from Implement a State Manager, but each UI element is defined in a separate component. The state of the counter is maintained in the smCounter state manager, and shared across all components.

It's a best practice to define your state manager in a separate API module component. That makes it easy to import the state manager into any component that uses it.

The state manager component has no user interface, and no .html template file.

The multiCounter component creates an instance of a shared state manager in the JavaScript file, and composes together the child components that make up the user interface in its .html template file.

The user interface for viewing and interacting with the counter state manager is made up of four components, each of which retrieves the shared smCounter state manager using the fromContext function.

The c-counter-display component retrieves the shared counter and displays the count and doubleCount values.

The c-counter-increment component retrieves the shared counter and provides a button that calls the increment() action.

The c-counter-set component retrieves the shared counter, takes a numeric input, and calls setCount() to set the value.

The c-counter-debug-state component retrieves the shared counter and renders the entire state as formatted JSON for debugging.