Reactivity for Fields, Objects, and Arrays
If a field’s value changes, and the field is used in a template or in a getter of a property that’s used in a template, the component rerenders and displays the new value. If a field is assigned an object or an array, the framework observes some changes to the internals of the object or array, such as when you assign a new value.
When a component rerenders, the expressions used in the template are reevaluated and the renderedCallback()
lifecycle hook executes.
Primitive fields—like booleans, numbers, and strings—are reactive. LWC tracks field value changes in a shallow fashion. Changes are detected when a new value is assigned to the field by comparing the value identity using ===
.
When manipulating complex types like objects and arrays, you must create a new object and assign it to the field for the change to be detected.
To deeply track mutations made to the field value for a complex object, use the @track
decorator.
To observe changes to the properties of an object or to the elements of an array, decorate the field with @track
.
When a field is decorated with @track
, Lightning Web Components tracks changes to the internal values of:
- Plain objects created with
{}
- Arrays created with
[]
The framework observes mutations made to plain objects and arrays in a recursive fashion, including nested objects, nested arrays, and a mix of objects and arrays. Cyclic references are also handled.
However, the framework doesn't observe mutations made to complex objects, such as objects inheriting from Object
, class instances, Date
, Set
, or Map
.
To tell the framework to observe changes to the properties of an object, decorate the field with @track
.
As discussed earlier, without using @track
, the framework observes changes that assign a new value to the field. If the new value is not ===
to the previous value, the component rerenders.
For example, let's change the code a bit to declare the fullName
field, which contains an object with two properties, firstName
and lastName
. The framework observes changes that assign a new value to fullName
.
This code assigns a new value to the fullName
field, so the component rerenders.
However, if we assign a new value to one of the object’s properties, the component doesn't rerender, because the properties aren’t observed.
The framework observes changes that assign a new value to the fullName
field. This code doesn't do that, instead it assigns a new value to the firstName
property of the fullName
object.
To tell the framework to observe changes to the object's properties, decorate the fullName
field with @track
. Now if we change either property, the component rerenders.
If a property contains an object, to track changes on the object's properties, annotate the property with @track
. To understand, let's modify our example. Our example initializes firstName
and lastName
to empty strings, which are primitive values, so the component rerenders when they change.
A component rerenders only if a property accessed during the previous rendering cycle is updated, even when the object is annotated with @track
. This prevents the component from rerendering excessively,
Consider this tracked object and a getter that prints the object’s properties.
During the first render cycle, the framework records that obj.value1
is accessed. Any mutation to obj
that doesn’t affect value1
is ignored since it doesn’t impact the rendered content. Therefore, a change to value1
triggers a rerendering, but adding a new property to obj
or a change to value2
doesn’t trigger a rerendering.
To rerender your component when adding a new property, assign the object to a new object with both values.
Another use case for @track
is to tell the framework to observe changes to the elements of an array.
If you don’t use @track
, the framework observes changes that assign a new value to the field.
The component rerenders when you assign a new value to arr
.
However, if we update or add an element in the array, the component doesn’t rerender.
To tell the framework to observe changes to the array’s elements, decorate the arr
field with @track
Additionally, the framework doesn’t automatically convert the array to a string for updates to the array’s elements. To return the updated string, use a getter to convert the elements of the array to a string using join()
.
Let’s look at a component with a field, x
, of type Date
. The template has a few buttons that change the internal state of x
. This example highlights that new Date()
creates an object, as it is not a plain JavaScript object internal state mutation will not be observed by the LWC engine, even though the code uses @track
.
Similarly to our previous example, the template has a few buttons that change the internal state of x
.
When you click the Init button, the change is observed and the template is rerendered. Lightning Web Components observes that x
is pointing to a new Date
object. However, when you click Update, the template isn’t rerendered. Lightning Web Components doesn’t observe changes to the value of the Date
object.
To ensure that the template is rerendered when the value changes, clone the existing date and update its value.
When you set a property to a value that can’t be tracked, a warning is logged. If you’re trying to debug a scenario where the component isn’t rerendering on change, look in your browser console. For our example, the browser console logs this helpful warning:
Property "x" of [object:vm TrackDate] is set to a non-trackable object, which means changes into that object cannot be observed.
When you enter a value in the First Name or Last Name field, the component converts it to uppercase and displays it.
The component’s class defines the firstName
and lastName
fields. Because they’re used in the getter of a property that’s used in the template (uppercasedFullName
), the component rerenders when their values change.
Fields are reactive. Expando properties, which are properties added to an object at runtime, aren’t reactive.
See the helloExpressions component in the lwc-recipes
repository.
See Also