How do I invoke a value change handler in LWC?

Is there a way in LWC to declare an attribute value change handler like there was in Aura? My use case is that I have a pager component which has 2 @api properties. When either of the @api values are changed, i’d like to reset the state of the component.

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

export default class Pager extends LightningElement {
    @api pagerData;  // rows of data being paged
    @api pageSize;   // the size of the page

    @track currentPage;  // 1s based page number (i.e. 1 is first 'index')

    ... 
    // (other properties that get number of pages, etc.) 
    ...

}

So when, for example, pageSize is changed by the parent component, then I’d like to reset the currentPage to 1.

To be a little more explicit, the parent component would have something like this in their markup / js

// testpage.html ----------------------------------------
<template>
    <c-pager page-data={myQueryResult} page-size=2></c-pager>
</template>

// testpage.js ------------------------------------------
import { LightningElement, api, track } from 'lwc';

    ...

    onSomeEvent(event){
       let pagerCmp = this.template.querySelector('c-pager');
       pagerCmp.pageSize = 3;
       //------------------------
       // pagerCmp should handle changes to its own internal state, 
       //  in this case it should reset its currentPage property to 1
       //------------------------
    }

    ...

}

Basically i’d like to write some code in the pager component to call a setup() function when the value of pageSize is changed, since the change to the @api parameters will warrant a reset in the internal state of the component. I’m not sure how to do this in LWC. Any ideas?

Thanks in advance.

Answer

You can use getters and setters like the following to detect changes when a parent component passes a new @api value.

Per the docs:

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.

Annotate either the getter or the setter with @api, but not both. It’s a best practice to annotate the getter.

Parent.html

<template>
  <lightning-input onchange={changePageSize}></lightning-input>
  <c-child page-size={pageSize}></c-test-child>
</template>

Parent.js

@track pageSize;
changePageSize(evt) {
  this.pageSize = evt.target.value;
}

Child.html

<template>
  current page size: {_pageSize}
</template>

Child.js

@api
get pagerData() {
  return this._pagerData;
}
set pagerData(value) {
  this.setAttribute('pagerData', value);
  this._pagerData = value;
  this.setup();
}

@api
get pageSize() {
  return this._pageSize;
}
set pageSize(value) {
  this.setAttribute('pageSize', value);
  this._pageSize = value;
  this.setup();
}

// private
@track _pagerData;
@track _pageSize;

connectedCallback() {
  this.setup();
}

setup() {
  console.log('hello');
}

Additional getter/setter examples here.

Furthur reading on the setAttribute function and what it actually does to a template.

As a FYI, renderedCallback() fires twice, and I’m not seeing any kind of private _isRendered check in your code so it’s probably worth moving it to connectedCallback() unless you have a strong reason to put it there.

Attribution
Source : Link , Question Author : Vincent Ip , Answer Author : Avalanchd

Leave a Comment