Reactivity

Note

Reactivity changed significantly in the Lightning Web Components Open Source 1.1.0 release.

All fields are reactive. If the value of a field changes and the field is used in a template or in the getter of a property used in a template, the component re-renders and the renderedCallback() lifecycle hook is called. When a component re-renders, all the expressions used in the template are re-evaluated.

To make a field public and therefore available to a component's consumers as a property, decorate it with @api.

Field and property are almost interchangeable terms. A component author declares fields in a class. An instance of the class has properties, so to component consumers, a field is a property. In a Lightning web component, only fields that a component author decorates with @api are publicly available to consumers as object properties.

Tip

Decorators are a JavaScript language feature. The @api and @track decorators are unique to Lightning Web Components. A field can have only one decorator.

Public Property Reactivity

To expose a public property, decorate a field with @api. Public properties define the API for a component. An owner component that uses the component in its HTML markup can access the component’s public properties via HTML attributes.

If the value of a public property changes, the component’s template re-renders.

Let's look at a simple app in the playground. The example-todo-item component has a public itemName property. The example-todo-app component consumes example-todo-item and sets its property via the item-name attribute.

Property names in JavaScript are in camel case while HTML attribute names are in kebab case (dash-separated) to match HTML standards. In the example-todo-app template, the item-name attribute on example-todo-item maps to the itemName JavaScript property on example-todo-item.

In todoApp.html, change the value of the item-name attributes and watch the component re-render.

Let's walk through the code.

The TodoItem class imports the @api decorator from lwc. It declares an itemName field and decorates it with @api to make it public. This class is part of the example-todo-item component, where example is the namespace.

// todoItem.js
import { LightningElement, api } from 'lwc';
export default class TodoItem extends LightningElement {
    @api itemName = 'New Item';
}

The component’s template defines a single todo item.

<!-- todoItem.html -->
<template>
    <div>
        <label>{itemName}</label>
    </div>
</template>

A parent component, in this case example-todo-app, can set the itemName property on child example-todo-item components.

<!-- todoApp.html -->
<template>
    <div>
        <example-todo-item item-name="Milk"></example-todo-item>
        <example-todo-item item-name="Bread"></example-todo-item>
    </div>
</template>

The parent component can also access and set the itemName property in JavaScript.

// todoApp.js
const myTodo = this.template.querySelector('example-todo-item');
myTodo.itemName // New Item

Field Reactivity

All fields are reactive. When the framework observes a change to a field used in a template or used in the getter of a property used in a template, the component re-renders.

In this example, the firstName and lastName fields are used in the getter of the uppercasedFullName property, which is used in the template. When either field value changes, the component re-renders.

The firstName and lastName fields contain primitives.

firstName = '';
lastName = '';

There is a limit to the depth of changes that the framework observes. If a field contains an object value or an array value, the framework observes changes that assign a new value to the field. If the value that you assign to the field is not === to the previous value, the component re-renders. To tell the framework to observe changes to the properties of an object or to the elements of an array, decorate the field with @track.

Let's declare the fullName field, which contains an object with two properties. The framework observes changes that assign a new value to the field.

fullName = { firstName : '', lastName : ''};

This code changes the value of fullName, so the component re-renders.

this.fullName = { firstName: 'Jane', lastName: 'Doe' };

This code changes the value of firstName. The framework is not observing changes to firstName, so the component doesn't re-render. Remember, the framework is observing changes to the fullName field. This code doesn't assign a new value to fullName, instead it assigns a value to the firstName property.

this.fullName.firstName = 'Jane';

To tell the framework to observe changes to the properties of the fullName object, decorate the field with @track.

@track fullName = { firstName: '', lastName: '' };

Now when the firstName property changes, the component re-renders.

this.fullName.firstName = 'Jane';

Let's look at this code in the playground. Enter a first name and last name and watch the component re-render. Now remove @track and do the same. The component doesn't re-render.

Reactive Data Types

There is a limit to the depth of changes that the framework observes. The depth depends on the data type of the field.

Note

Fields are reactive. Expandos are not reactive.

Lightning Web Components observes changes to the internal values of these types of fields:

If a field contains a primitive data type, reactivity simply works as expected.

If a field contains an object value or an array value, it's important to understand how the framework tracks changes. As we saw in Field Reactivity, the framework doesn't observe changes to the properties of an object or to the elements of an array unless the field is decorated with @track.

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() is an object, but it is not created via {}, so the internals of the object are not tracked.

When you click the Init button, the x field is assigned a new Date object and the template re-renders. However, when you click Update, the template doesn't re-render because the framework doesn’t track changes to the value of the Date object.

Note

When you set a reactive 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 re-rendering 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.