[SalesForce] refresh child components from parent LWC

I have 3 components as child, parent and grandparent. From child component, I dispatch an event using bubbles: true, composed: true, intimating the grandparent to refresh. In the grandparent, I use refreshApex and get the updated data from the back-end (all this verified).

However, the updated data is not passed to it's child and grandchild components.

Any idea how I can achieve this? I need to get the updated data in child and grandchild components.

Code Snippet:
grandParent.js

export default class GrandParent extends LightningElement {
   lstMyData = [];
   wiredResult;
   @wire(GET_DATA)
   myList(result){
      this.wiredResult = result;
      if(result.data){
          this.lstMyData = result.data
      }
   }
   
   handleNewFeedback(event){
        return refreshApex(this.wiredResult);
   }
}

grandParent.html:

<template>
  <div class="header_bgcolor">
    My Contacts Data
    <template for:each={lstMyData} for:item="contactData">
       <c-each-contact-data key={contactData.recId} obj-contact-Data={contactData} onfeedback={handleNewFeedback}></c-mbo-contact-objective>
    </template>
    </div>
</template>

parent.js

export default class Parent extends LightningElement {
      @api objContactData;
      myContactData;
      showFeeback = false;
      connectedCallBack(){
        this.myContactData = Object.assign({},this.objContactData);
      }
      showFeedbacks(event){
        this.showFeeback =  !this.showFeeback;
      }
}

parent.html

<template>
    <div for:each={myContactData} for:item="contactData" key={contactData.recId}>
        <p>Name : contactData.Name</p>
        <p onclick={showFeedbacks}>Feedback Count : contactData.countOfFeedbacks</p>
        <div if:true={showFeeback}>
            <c-grand-child contact-id={contactData.recId}></c-grand-child>
        </div>
    </div>
</template>

grandChild.js

import GET_DATA from '@salesforce/apex/ClassName.methodName';
import DELETE from '@salesforce/apex/ClassName.methodName1';
import refreshApex from '@salesforce/apex';
export default class GrandChild extends LightningElement {
      @api contactId;
      feedbackList = [];
      wiredResult;
      wire(GET_DATA, {contactId : '$contactId'})
      feedbacks(result){
          this.wiredResult = result;
          if(result.data){
              this.feedbackList = result.data;
          }
      }
      
      deleteFeedback(event){
          let feedbackId = event.target.getAttribute('data-id');
          DELETE({feebackId : this.feedbackId})
          .then(result=>{
              return refreshApex(this.wiredResult);
              this.dispatchEvent(new CustomEvent('feedback',bubbles:true,composed:true));
          })
      }
      
}

grandChild.html

<template>
    <div for:each={feedbackList} for:item="fdbck" key={fdbck.Id}>
        <p>Provider : fdbck.Name</p>
        <p>Comment : fdbck.Comment</p>
        <p data-id={fdbck.Id} onclick={deleteFeedback}>DELETE</p>
    </div>
</template>

the console log of lstMyData shows updated feedback count after refreshApex. However, the same is not reflected in it's child (parent component in our case).

Best Answer

Your problem is in the parent component logic. The component only copies the data on initial render/connection, not afterwards. Change your controller to:

export default class Parent extends LightningElement {
    @api set objContactData(value) {
        this.myContactData = [...value];
    }
    get objContactData() {
        return this.myContactData;
    }
    myContactData;
    showFeeback = false;
    connectedCallBack(){
        this.myContactData = Object.assign({},this.objContactData);
    }
    showFeedbacks(event){
        this.showFeeback =  !this.showFeeback;
    }
}

This way, each time the grandparent changes the data, you'll get a fresh copy.

Related Topic