Data Guidelines
Lightning Data Service
State Management Compared with Alternatives
Share a State Manager Across Components
Nested State Managers
State Management Examples
Call APIs from Apex
Work with Errors
Develop Secure Code
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:
And the consumer component:
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.
Tip
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.
Tip
The state manager component has no user interface, and no .html template file.
1import { defineState } from '@lwc/state';
2
3const counterManager = defineState(
4 ({ atom, computed, setAtom }, initialValue = 0) => {
5
6 // Create reactive value
7 const count = atom(initialValue);
8
9 // Create computed (and reactive) value
10 const doubleCount = computed([count], (countValue) => countValue * 2);
11
12 // Create a function that increments the count value
13 const increment = () => {
14 setAtom(count, count.value + 1);
15 };
16
17 // Create a function that directly sets the count atom
18 const setCount = (newValue) => {
19 setAtom(count, Number(newValue));
20 };
21
22 // This defines the API shape of counterManager
23 return {
24 count,
25 doubleCount,
26 increment,
27 setCount,
28 };
29 }
30);
31export default counterManager;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.
1import { LightningElement } from 'lwc';
2import counterManager from 'c/smCounter';
3
4export default class MultiCounter extends LightningElement {
5 counter = counterManager(100);
6}1<template>
2 <lightning-card title="Example: Multi-Counter">
3 <c-counter-display></c-counter-display>
4 <c-counter-increment></c-counter-increment>
5 <c-counter-set></c-counter-set>
6 <c-counter-debug-state></c-counter-debug-state>
7 </lightning-card>
8</template>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.
1import { LightningElement } from 'lwc';
2import { fromContext } from '@lwc/state';
3import counterManager from 'c/smCounter';
4
5export default class CounterDisplay extends LightningElement {
6 counter = fromContext(counterManager);
7}1<template>
2 <section class="slds-p-around_small">
3 <p>Count: {counter.value.count}</p>
4 <p>Doubled: {counter.value.doubleCount}</p>
5 </section>
6</template>The c-counter-increment component retrieves the shared counter and provides a button that calls the increment() action.
1import { LightningElement } from 'lwc';
2import { fromContext } from '@lwc/state';
3import counterManager from 'c/smCounter';
4
5export default class CounterIncrement extends LightningElement {
6 counter = fromContext(counterManager);
7
8 increment() {
9 this.counter.value.increment();
10 }
11
12}1<template>
2 <section class="slds-p-around_small">
3 <lightning-button label="Increment" onclick={increment}></lightning-button>
4 </section>
5</template>The c-counter-set component retrieves the shared counter, takes a numeric input, and calls setCount() to set the value.
1import { LightningElement } from 'lwc';
2import { fromContext } from '@lwc/state';
3import counterManager from 'c/smCounter';
4
5export default class CounterSet extends LightningElement {
6 counter = fromContext(counterManager);
7
8 setCount() {
9 this.counter.value.setCount(Number(this.refs.newValue.value));
10 }
11
12}1<template>
2 <section class="slds-p-around_small">
3 <lightning-input type="text" label="Counter"
4 value={counter.value.count} lwc:ref="newValue"></lightning-input>
5 <lightning-button label="Set Counter" onclick={setCount}></lightning-button>
6 </section>
7</template>The c-counter-debug-state component retrieves the shared counter and renders the entire state as formatted JSON for debugging.
1import { LightningElement } from 'lwc';
2import { fromContext } from '@lwc/state';
3import counterManager from 'c/smCounter';
4
5export default class CounterDebugState extends LightningElement {
6 counter = fromContext(counterManager);
7 get stateDump() {
8 return JSON.stringify(this.counter.value, null, 2);
9 }
10
11}1<template>
2 <hr/>
3 <section class="slds-p-around_small">
4 <textarea rows="10" cols="40">{stateDump}</textarea>
5 </section>
6 <hr/>
7</template>