How to Observe Property Changes with LitElement and TypeScript
Tags: litelementtypescript
Published: 2021-08-03 13:53:22

---
title: How to Observe Property Changes with LitElement and TypeScript
published: true
date: 2021-03-03 17:00:46 UTC
tags: litelementtypescript
canonical_url: https://www.thisdot.co/blog/how-to-observe-property-changes-with-litelement-and-typescript
---

In a previous post, I explained [how to manage LitElement properties](https://labs.thisdot.co/blog/litelement-properties-property-vs-internalproperty) using `@property` and `@internalProperty` decorators through TypeScript. Also, I explained how LitElement manages the declared properties and when it triggers an update cycle, causing the component to re-render its defined template.

A couple of days ago, I saw an interesting question regarding these property changes on a developer website:

> How to observe @property changes in a LitElement component?

In this article, I will explain how you can observe (and "react" maybe?) these properties considering the lifecycle of a LitElement component.

## A Practical Use Case

Let's suppose you're building a small application that needs to display a list of _Users_. You can select any of them and other details should be displayed. In this case, the _Address_ information is displayed.

[![user-addresses](https://images.ctfassets.net/zojzzdop0fzx/FcsaVGBcN2sIZMpAKWG2f/88a8f04b81f060fbfc3490e35ca71973/user-addresses.png)](https://images.ctfassets.net/zojzzdop0fzx/FcsaVGBcN2sIZMpAKWG2f/88a8f04b81f060fbfc3490e35ca71973/user-addresses.png)

### First Steps

Instead of creating a full project from scratch, we'll take the [@property vs @internalProperty demo](https://stackblitz.com/edit/lit-element-property-internalproperty) as the starting point. Open the link and create a fork to follow up with next steps.

### Project Content

Once you complete the _fork_, you'll have a project ready with:

- The Data Model, using TypeScript interfaces and static typing.
- A Data Set, which exports two constants for easy testing: `users` and `address`.
- 

Two Web Components using LitElement

  - The `MainViewer` Component, which is a _container_ component that will be in charge of displaying both the list of users and their details.
  - The `AddressViewer` Component, which is a _child_ component that receives the `userId` selected in the main list.

### Property Changes

Let's take a closer look at the existing components to understand the main properties that will be considered later.

The `main-viewer.ts` file defines the following template:

```
<div>
  <h1>User Address Viewer</h1>
  <!-- The User list is rendered here -->
  <!-- Child component rendered below -->
  <address-viewer .userId=${this.userId}></address-viewer>
</div>
```

The code snippet shows the relevance of the `userId` property, declared as part of the `MainViewer` component. Every time it changes, the render function will be triggered, sending the new property value to its child component(`<address-viewer userId=${newValue}>`).

On the other hand, the `address-viewer.ts` file defines the **public property** `userId` and determines the need of an internal property to have the `Address` object:

```
// address-viewer.ts

@customElement("address-viewer")
class AddressViewer extends LitElement {
  @property({ type: Number }) userId: number;
  @internalProperty() userAddress?: Address;
}
```

Let's focus on the `userId` property of the `AddressViewer` component to understanding how we can observe the changes.

## Observing the Property Changes

When a new value is set to a declared property, it triggers an update cycle that results in a re-rendering process. While all that happens, different methods are invoked and we can use some of them to know what properties have been changed.

### The Property's setter method

This is the first place where we can observe a change. Let's update the `@property()` declaration in the `address-viewer.ts` file:

```
// address-viewer.ts

@customElement("address-viewer")
class AddressViewer extends LitElement {

  @property({ type: Number }) set userId(userId: number) {
    const oldValue = this._userId;
    this._userId = userId;
    this.requestUpdate("userId", oldValue);
  }

  private _userId: number;

  constructor() {
    super();
  }

  get userId() {
    return this._userId;
  }
}
```

As you can see, we just added both `set` and `get` methods to handle the brand new `_userId` property. Creating a new private property is a common practice when the accessor methods are used. The underscore prefix for `_userId` is also a code convention only.

However, since we're writing **our setter method** here, we may need to call the `requestUpdate` method manually to schedule an update for the component. In other words, LitElement generates a setter method for declared properties and call the `requestUpdate` automatically.

### The `requestUpdate` method

This method will be called when an element **should be updated** based on a component state or property change. It's a good place to "catch" the new value before the `update()` and `render()` functions are invoked.

```
// address-viewer.ts

requestUpdate(name?: PropertyKey, oldValue?: unknown) {
  const newValue = this[name];
  // You can process the "newValue" and "oldValue" here
  // ....
  // Proceed to schedule an update
  return super.requestUpdate(name, oldValue);
}
```

In this case, the method will receive the name of the property that has been changed along with the previous value: `oldValue`. Make sure to call `super.requestUpdate(name, oldValue)` at the end.

### The `update` method

This is another option to observe the property changes. See the example below:

```
update(changedProperties: Map<string, unknown>) {
  if (changedProperties.has("userId")) {
    const oldValue = changedProperties.get("userId") as number;
    const newValue = this.userId;
    this.loadAddress(newValue);
  }
  super.update(changedProperties);
}
```

The `update` method has a slightly different signature compared with the previous options:

The **changedProperties** parameter is a `Map` structure where the **keys** are the names of the declared properties and the **values** correspond to the old values (the new values are set already and are available via `this.<propertyName>`).

You can use the `changedProperties.has("<propName>")` to find out if your property has been changed or not. If your property has been changed, then use `changedProperties.get("<propName>")` to get access to the old value. A [type assertion](https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions) is needed here to match the static type defined by the declared property.

It can be overridden to render and keep updated the DOM. In that case, you must call `super.update(changedProperties)` at the end to move forward with the rendering process.

If you set a property inside this method, **it won't trigger another update**.

### The `updated` method

The signature of this method will match with the `update`. However, this method is called when the element's DOM **has been updated and rendered**. Also, you don't need to call any other method since you're at the end of the lifecycle of the component.

```
updated(changedProperties: Map<string, unknown>) {
  console.log(`updated(). changedProps: `, changedProperties);
  if (changedProperties.has("userId")) {
    // get the old value here
    const oldValue = changedProperties.get("userId") as number;
    // new value is 
    const newValue = this.userId;
  }

  // No need to call any other method here.
}
```

You can use this method to:

- Identify a property change after an update
- Perform post-updating tasks or any processing after updating.

As a side note, if you set a property inside this method, it will trigger the _update_ process again.

## Live Demo

Want to play around with this code? Just open the Stackblitz editor:

<iframe src="https://stackblitz.com/edit/lit-element-property-changes?file=src%2Faddress-viewer.ts" width="100%" height="500px" title="Lit Element and TypeScript"></iframe>

Feel free to reach out on [Twitter](https://twitter.com/luixaviles) if you have any questions. Follow me on [GitHub](https://github.com/luixaviles) to see more about my work.

* * *

_This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit [thisdotlabs.com](https://www.thisdotlabs.com)._

_This Dot Media is focused on creating an inclusive and educational web for all. We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit [thisdot.co](https://www.thisdot.co)._