Lightning Web Component Reactive Properties

Lightning Web Components have quite a lot going on behind the scenes. One of the newer changes is the introduction of reactive properties. Reactive properties are variables you declare in components that fire page rerenders when changed. These variables can be specific to a single component, or they can be passed into children components, which creates a framework for sophisticated app design.

Reactive properties have changed since LWC’s inception, so this post will cover the new developments!

Natively Reactive Properties

Let’s start with a simple example to show LWC reactivity. I’m making a component that displays a user input on a page:

LWC Javascript

import { LightningElement } from 'lwc';

export default class Reactive extends LightningElement {
    reactiveText = '';

    assign(e){
        this.reactiveText = e.target.value;
    }
}

LWC HTML

<template>
    <div>
        <input type="text" name="ReactivePropertyValue" onkeyup={assign}/>
        <br/>
        {reactiveText}
    </div>
</template>

This is as simple as it gets. This component has a single input, ReactivePropertyValue, whose value gets assigned to the reactive property ‘reactiveText.’ reactiveText is displayed under the input:

lwc input

When you type a value into this input field, it will display under in and update in real time! This is reactivity in action!

lwc reactive input

This may seem simple, but this is what makes sophisticated web apps. Any primitive data type declared in a Lightning Web Component is reactive. But, what is happening behind the scenes? Salesforce is rerendering the entire template when a reactive property changes. This also fires any renderedCallback() methods. renderedCallback() is a lifecycle hook that fires after the page as been rendered. It can be used to make some really fancy apps.

To demonstrate what is happening, if I add a renderedCallback() to my component that does a console log everytime it is fired:

    renderedCallback(){
        console.log('I rendered');
    }
lwc renderedCallback

The component initially fired a renderedCallback when it finished loading and then one event everytime I typed, updating the value of the reactive property reactiveText. Use this to your advantage.

Declaring Reactive Properties

Primitive data types are reactive by default, but arrays and objects are not. I’ll adjust my component to use an object:

LWC Javascript

import { LightningElement} from 'lwc';

export default class Reactive extends LightningElement {
    reactiveText = {text1:'', text2:''};

    renderedCallback(){
        console.log('I rendered');
    }

    assign(e){
        this.reactiveText.text1 = e.target.value;
    }
}

LWC HTML

<template>
    <div>
        <input type="text" name="ReactivePropertyValue" onkeyup={assign}/>
        <br/>
        {reactiveText.text1}
    </div>
</template>
lwc reactive object

In this case, the object did not rerender even though we are targeting a key-value pair inside it. To get an object (and array or list) to also be reactive, tag it with the ‘@track’ property. Make sure to import ‘track’ from ‘lwc’:

import { LightningElement, track } from 'lwc';

export default class Reactive extends LightningElement {
    @track reactiveText = {text1:'', text2:''};

    renderedCallback(){
        console.log('I rendered');
    }

    assign(e){
        this.reactiveText.text1 = e.target.value;
    }
}
lwc reactive object display

Cross-Component Reactive Properties

With Lightning Web Components, you can pass property values from parent to child. Properties that are configured this way have the reactive decorator ‘@api.’ This also means they follow the same reactive behavior as natively reactive or ‘@track’ decorated properties.

Say we pass in the value of reactiveText.text1 to another component that renders it, instead of rendering it in our component:

reactive LWC

<template>
    <div>
        <input type="text" name="ReactivePropertyValue" onkeyup={assign}/>
        <br/>
        <c-reactive-display display-text={reactiveText.text1}></c-reactive-display>
    </div>
</template>

import { LightningElement, track } from 'lwc';

export default class Reactive extends LightningElement {
    @track reactiveText = {text1:'', text2:''};

    renderedCallback(){
        console.log('I rendered');
    }

    assign(e){
        this.reactiveText.text1 = e.target.value;
    }
}

reactiveDisplay LWC

<template>
    <div>
        {displayText}
    </div>
</template>

import { LightningElement, api } from 'lwc';

export default class ReactiveDisplay extends LightningElement {
    @api displayText = '';

    renderedCallback(){
        console.log('Child component rendered');
    }
}

Now, reactive is simply an input that sets the value for reactiveText.text1. reactiveDisplay is a child component of reactive that displays the value of displayText. displayText is tagged with the ‘@api’ decorator and it is assigned the value of reactiveText1.text1 in reactive’s HTML.

What happens when you start typing?

lwc api property

reactiveDisplay displays the text! I added a renderedCallback() in both components so you can also see that the renderedCallback(), and thus the rerendering behavior, or each component.

Leave a Reply

%d bloggers like this: