Best Practices for Reactivity in Screen Flows
Ensure your Lightning web components integrate well within the Flow runtime engine and work as expected for screen flow reactivity.
These sections describe state management with reactivity in screen flows, including:
- How LWC state management differs from Aura
- How state management works in the new LWC Flow Runtime
- State management and event best practices to follow when building LWCs for your screen flows
State management for Aura components and LWCs follow different paradigms. In Aura components, <aura:attributes />
are viewed and modified by the component to which they belong, including external components. LWCs are designed to have a clear separation between internal and external states. Variables declared with @api
are intended to be modified only by the LWC’s parents. Variables without the @api
annotation and variables with @track
are considered internal states and can be changed within the LWC that defines them.
The Flow runtime adheres to LWC state management design principles. It works best when the LWC components that it renders do not modify their own @api
attributes. Instead, components request a change by firing FlowAttributeChangeEvent
, which is essential for several reasons.
- Components that allow their parents to control their
@api
properties are more easily reused because they allow the application to control the state rather than the component’s own internal business logic. - By firing attribute change events, the Flow runtime maintains an accurate picture of the component state. The Flow runtime can control inter-component interactions such as conditional field visibility, which is part of reactivity.
Using LWC linter rules is an excellent way to ensure that your component does not modify @api
properties. Set up a linter that uses LWC’s linter rules in your development environment, specifically ensuring that the setting is enforced.
To maintain consistent behavior between Flow Runtime versions, when a user clicks the Next or Finish button, the Flow Runtime collects values by extracting each custom LWC component’s current @api
attribute state before moving to the next screen or finishing the flow. This mechanism ensures that components that do not adhere to the prescribed design pattern continue to work in the latest Flow Runtime version. It is strongly recommended to avoid relying on this behavior.
Here is an example of a simple text input component that demonstrates firing FlowAttributeChangeEvent
instead of updating the LWC’s @api
property:
The following example isn’t recommended.
The FlowAttributeChangeEvent
event provides LWCs the ability to inform the runtime of their current state. It is part of Flow Runtime’s state management mechanism. These events are meant to be fired whenever user interaction results in a modification that requires an update to an @api
property.
Import FlowAttributeChangeEvent
from the lightning/flowSupport
module.
Next, you can create an instance of the event.
FlowAttributeChangeEvent
includes two arguments.
apiParameterName
—The name of the API property to modify.updatedValue
—The updated value for the Flow attribute.
Pass a string to apiParameterName
that matches the API property as it appears in the JavaScript and .js-meta.xml
files.
For example, notice how exampleApiParameterName
is consistent in all 3 places:
-
Declare the attribute in the LWC’s
.js-meta.xml
file: -
Declare the API property in the LWC’s
.js
file: -
Construct the
FlowAttributeChangeEvent
instance:
Pass the updatedValue
to match the declared datatype in the .js-meta.xml
file.
-
Declare the property as a Record:
-
Declare the property as a string:
-
Declare the property as a number:
-
Declare the property as a boolean:
After constructing a FlowAttributeChangeEvent
instance, you can fire the event using the this.dispatchEvent(eventToFire)
method.
Follow these FlowAttributeChangeEvent
examples and steer clear of problematic usages to reduce unexpected behaviors after a flow version upgrade. These best practices enable you to maintain your components more easily.
- Fire events in event handlers or in methods invoked within event handlers.
- Limit an event’s value parameter to the following datatypes: String, number, boolean, JSON (for record types).
- Ensure that the Flow datatype of the event’s value parameter matches the datatype of the LWC’s
@api
property. - React to changes to
@api
properties by using a get/set pattern where appropriate.
This example demonstrates using a getter and setter to count the updates with an @api
property vs. incrementing a counter before firing an attribute change event. Both patterns are valid, but they achieve slightly different results.
In this case, this.changeCounter
is incremented whenever the user enters a change in the text input and whenever the Flow Runtime determines that the textValue
property has changed. This approach accounts for component initialization and cross-component interactions via reactivity. Use an intermediate variable to render updates from the set method. In this example, textValueToRender
fills this role.
The following code example increments a counter before firing an attribute change event. This scenario doesn’t use the get/set pattern.
In this case, this.changeCounter
is incremented whenever the user inputs a change in the text input. The counter does not update when reactivity changes the value of this field.
Use annotationless or @track
properties to maintain a modifiable local component state.
The following example shows a color picker that presents the user with an input text box and some swatches. The color picker component returns color
to the flow, so only color
needs the @api
annotation. The selectedSwatchId
and inputValue
members are internal only and do not need any decorator.
Use a get method to combine multiple @api
properties together to construct a derived variable for the view.
Apply the same logic in the set method of every @api
property that contributes to a derived member variable needed for internal component state management, as in more complex scenarios than the previous example. This logic ensures that a change made to any contributing attribute causes the value to render correctly regardless of the order of the set attributes.
Don’t modify @api
properties. Fire FlowAttributeChangeEvents
instead.
Don’t modify FlowAttributeChangeEvent
parameters after construction. By default, FlowAttributeChangeEvent
instances are composed and bubble.
Avoid firing FlowAttributeChangeEvents
and FlowNavigationXxx
events at the same time as it can lead to race conditions. There's never a guarantee that your Lightning web component has had time to render the updated values by the time the navigation process is started.
In a flow component, a derived attribute is an attribute whose value is determined by other attributes of the same component. The value may be tied to one or more other attributes. For example, the firstSelectedRow
parameter of the Data Table component is a derived attribute because its value is determined by the value of the selectedRows
attribute. Specifically, the value of the firstSelectedRow
parameter is the value of the first element of the selectedRows
attribute.
When you create a flow screen component that includes a derived attribute, follow the best practices outlined here to ensure that the component functions properly within the reactivity framework. With these best practices, you can avoid issues related to:
A reactivity chain occurs when a change to an attribute of one component results in a cascade of changes to attributes of other components. For example, a user selects a row in a Data Table component, which changes the value of the firstSelectedRow
attribute of the component, which changes the firstName
attribute of a Name component. The change to the Name component changes the value
attribute of a Text component.
When an admin configures a screen, you choose whether the flow preserves user-specified values when the user leaves and then returns to the screen using the Previous or Next button.
To ensure that derived attributes behave as expected in flow screens with reactive components, follow these guidelines:
-
Do fire a
FlowAttributeChangeEvent
event for the derived attribute in theset
method of the attribute that drives the change. Also fire aFlowAttributeChangeEvent
event for the derived attribute in theconnectedCallback
method of the component, for example: -
Don't omit the
FlowAttributeChangeEvent
event in theconnectedCallback
method. You must include theFlowAttributeChangeEvent
event in two locations. When the flow executes theFlowAttributeChangeEvent
in theset
method of the attribute that drives the change, the component isn't in the DOM and the event essentially goes nowhere. Adding the event in theconnectedCallback
method ensures that when the flow adds the component to the DOM, the flow runtime can consume the event. -
Don't omit the
FlowAttributeChangeEvent
event in theset
method. Firing theFlowAttributeChangeEvent
in theset
method of the attribute that drives the change ensures that changes are applied to both the attribute that drives the change and the derived attribute. Consequently, reactivity chains that include a custom component function properly. -
Don't omit the
connectedCallback
method by usingPromise.resolve().then(...)
to delay firing theFlowAttributeChangeEvent
inside theset
method. Delaying theFlowAttributeChangeEvent
ensures that the component is in the DOM when the flow first calls theset
method. However, the delay also means that the component fires the event after the flow initializes the screen. Consequently, the flow runtime always overrides preserved values and overrules the setting for revisited value.
In addition to FlowAttributeChangeEvent
, your Lightning web components can fire navigation events to move between screens, pause, or finish flows. To work with navigation events, import these functions from lightning/flowSupport
:
FlowNavigationNextEvent
FlowNavigationBackEvent
FlowNavigationPauseEvent
FlowNavigationFinishEvent
This snippet describes how to import and fire a navigation event. While the example uses FlowNavigationNextEvent
, the same structure holds for each of the available navigation targets.
Navigation events allow your Lightning web components to include additional control mechanisms. Fire navigation events when reacting to end-user interactions. Refer to these guidelines when using navigation events:
- Fire navigation events in event handlers (or in the method they invoke).
- Don’t fire navigation events outside of event handlers because it leads to a poor user experience.
- Don’t fire navigation events in lifecycle handlers like
renderedCallback
andconnectedCallback
. - Place the logic for skipping screens in Flow Decision Nodes, not in the lifecycle of a screen.
- Don’t perform navigation in a dummy screen because it can lead to poor Flow performance.
- If you encounter a scenario where navigation must be done within the initialization phase of the screen because data isn’t yet available to Flow Decision Nodes, post a request for an upgrade in Idea Exchange