Lightning Web Component Reactive Properties

Let’s understand here what is Reactive Properties. If the value of reactive property changes, the component’s template rerenders. When you modify reactive property the template view updates. A reactive property can be public or private.

Public Reactive Properties

Public properties define the public API for a component. A parent component that uses the component in its markup can access the
component’s public properties. Public properties are reactive. If the value of reactive property changes, the component’s template
rerenders any content that references the property. To mark a property as public, annotate it with the @api decorator. When you use the @api decorator, you must import it explicitly from the engine. For example:

import { api } from 'LWC';

Use decorators to add functionality to a property. A property can have only one decorator. For example, a property can’t
have @api and @track (private reactive property) decorators.

Create a lightning web component using the below SFDX command. In  TodoItem class with an itemName public property.

sfdx force:lightning:component:create --type lwc --componentname todoitem --outputdir force-app\main\default\lwc

Use the below todoitem.html

<template>
    <div class="view">
        <label>{itemName}</label>
    </div>
</template>

Use the below todoitem.js . In this JavaScript class, we have an itemName public reactive property

import { LightningElement,api } from 'lwc';
export default class Todoitem extends LightningElement {
    @api itemName;
}

Use the below todoitem.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="todoitem">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
</LightningComponentBundle>

 

Create a Container component using the below SFDX command.  In this component, we will be passing the itemname.

 sfdx force:lightning:component:create --type lwc --componentname todoapp --outputdir force-app\main\default\lwc

Use below todoapp.html code. When you use the c-todoitem component in another component, you can set the itemName property. Property names in JavaScript are in camel-case while HTML attribute names are in kebab-case (dash-separated) to match HTML standards. In todoapp.html, the item-name attribute in markup maps to the itemName JavaScript property of c-todoitem.

<template>
    <div class="listing">
        <c-todoitem item-name="Milk"></c-todoitem>
        <c-todoitem item-name="Bread"></c-todoitem>
    </div>
</template>

 

A component that declares a public reactive property can’t set the property value, except at component construction time.
A parent component that uses the component in its markup can set the component’s public property value. In our example, the
c-todoitem component can’t update the value of the itemName property in the todoitem.js file.

 

Push the changes to scratch org and you can able to see the output as shown below.

 

 

To execute logic each time a public property is set, write a custom setter. If you write a setter for a public property, you must also write
a getter. Here’s an example of a setter that changes the item name to uppercase. To re-render the custom element when the setter is called, create a private reactive property. In this example, the private reactive property is itemNameUpper. This property is set in the setter and
returned by the getter. Annotate either the getter or the setter with @api, but not both. It’s a best practice to annotate the getter. If you declare a getter and a setter with @api decorators, don’t declare the public property explicitly.

 

import { LightningElement,api,track } from 'lwc';

export default class Todoitem extends LightningElement {
     @api
     get itemName() {
         return this._itemName;
     }

     set itemName(value) {
         this._itemName = value.toUpperCase();
     }
}

 

Push the changes and now you can able to see the output as shown below

 

Private Reactive Properties

To observe a component’s internal state and rerender when its state changes, decorate a property with @track. If the value of a tracked
property changes and it’s referenced in the template, the component rerenders. Tracked properties are also called private reactive
properties. The tracked property can be referenced directly in the template, or it can be used indirectly in a getter and setter.

Let’s look at some sample code to see how @track works.

You can use @track to decorate a property only; you can’t use it to decorate an object. It’s possible to decorate multiple properties
with @track. A property can have only one decorator. For example, a property can’t have both @track and @api.
Let’s look at some sample code to see how @track works.

Create a lightning web component using the below SFDX command.

sfdx force:lightning:component:create --type lwc --componentname child --outputdir force-app\main\default\lwc

Use the below child.html

<template>
    <div>Child value: {itemName}</div>
</template>

 

Use the below child.js code

import { LightningElement ,track,api} from 'lwc';

export default class Child extends LightningElement {
    @track itemNameUpper='Hello';
    @api
    get itemName() {
        return this.itemNameUpper;
    }
    set itemName(value) {
        this.itemNameUpper = value.toUpperCase();
    }
}

 

Use the below child.js-meta.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="child">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
</LightningComponentBundle>

 

In this example, the parent component consumes the child component. When you click the parent component, it updates
a tracked counter property, which causes the component to rerender.

sfdx force:lightning:component:create --type lwc --componentname parent --outputdir force-app\main\default\lwc

Use below parent.html code

<template>
    <div onclick={handleClick} class="listing">
        <div>
            Click to increment.<br>
            Expected: parent and child values update<br>
            {counter}
        </div>
        <br>
        <div>Parent changingGetter: {changingGetter}</div>
        <div>Parent changingVar: {changingVar}</div>
        <br>
        <c-child item-name={changingGetter}></c-child>
        <c-child item-name={changingVar}></c-child>
    </div>
</template>

Use below parent.js code

 

import {
    LightningElement,
    track
} from 'lwc';

export default class Parent extends LightningElement {
    @track counter = 1;
    changingVar = 'initial changingVar';
    get changingGetter() {
        return this.counter + ' changingGetter';
    }
    handleClick() {
        this.counter += 1;
        this.changingVar = this.counter + ' changingVar';
    }
}

Use below parent.js-meta.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="helloWorld">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

 

To see how tracked properties cause components to rerender. push changes and add this component to the page layouts. you can able to see the template is rerenders every time user click on the div.

 

 

 

 

In parent.js, remove @track from the counter property. Run the code and click the parent component. The parent component doesn’t re-render. The child component doesn’t re-render.  You can’t create a getter or setter for a private reactive property. Since the property is private, you can access or modify it directly in the code so a getter or setter isn’t needed. Update the code as below

import {
    LightningElement,
    track
} from 'lwc';

export default class Parent extends LightningElement {
   counter = 1;
    changingVar = 'initial changingVar';
    get changingGetter() {
        return this.counter + ' changingGetter';
    }
    handleClick() {
        this.counter += 1;
        this.changingVar = this.counter + ' changingVar';
    }
}

 

Push the changes to scratch org and now when you click on the div , the counter changes wnt be referlct to the UI and template wnt reredners .