2

I am passing a property from a parent to a child component. I am trying to perform a task every time the value passed from a component's parent to itself changes.

export default class displayData extends LightningElement 
{
   @api data;

   @track dataActive = []; //Used in HTML to display data.

     populateDataActive()
     {
         let dataActiveTemp = [];
         data.forEach((d)=> {
         if(valid(d))  // Some check to select only specific items of the array passed 
            dataActiveTemp.push(d);
         })
         this.dataActive = dataActiveTemp;
     }
     
    connectedCallback() //Gets called once when 'displayData' is inserted into DOM.
    {
         this.populateDataActive();
    }
     
     renderedCallback() //This leads to an infinite loop and the app crashes.
     {
        this.populateDataActive();

     }
     
     onRecordClick(event) //This child to parent event results in rerender of the whole component with updated 'data' api variable.
    {
        const id = event.currentTarget.getAttribute('data-item');
        const clickEvent = new CustomEvent('fetchData', { detail: id });
        this.dispatchEvent(clickEvent);
        console.log('Clicked data element with id: ', id);
    }
     
  }

 

How do I ensure that the populateDateActive() function is called if and only if @api data changes (from the parent)? connectedCallback() gets called only once, while renderedCallback() goes into an infinite loop when I call populateDataActive() from it, and the app crashes. I thought one could update @api variable directly but it turns out that's a read-only property.

2
  • 1
    @glls As I've recently discovered, getter/setter isn't reactive, meaning that it may not work as expected in this case.
    – sfdcfox
    Commented Jun 2, 2022 at 16:30
  • It seems to work fine as of now. I have modified my code as suggested in the answer that you linked to and it does exactly what it should. I'll update if run in to any issues. Commented Jun 2, 2022 at 16:58

1 Answer 1

3

The new practice I've used for tracking these kinds of changes properly is to use renderedCallback with an asynchronous delay and a flag to prevent infinite recursion:

_rendering;
async renderedCallback() {
    if(this._rendering) {
      return;
    }
    this._rendering = true; // Ignore next renderedCallback
    this.populateDataActive();
    await Promise.resolve(); // wait for render to happen
    this._rendering = false; // Next renderedCallback is legit
 }
 

Note that this may not work if a render cycle isn't triggered; this means that your value may need to be in the markup somehow. I've taken to just using slds-hide on a span, and put the @api value inside this hidden element.

<span class="slds-hide">{data}</span>
2
  • Interesting approach. While the getter / setter approach seems to have (temporarily?) solved my problem, is this exactly an efficient approach? Commented Jun 2, 2022 at 16:59
  • 1
    @darthcoderUS The getter/setter method works as long as you don't have child components that also need to be updated. In other words, the getter/setter properties are not reactive. Aside from that, they work just fine.
    – sfdcfox
    Commented Jun 2, 2022 at 19:35

Not the answer you're looking for? Browse other questions tagged .