[SalesForce] Updating the Data for an LWC lightning-datatable isn’t re-rendering the lightning-datatable

I'm running into an issue getting a lightning-datatable to rerender when I update the data attribute.

My LWC contains 3 buttons for testing and a lightning-datatable that is connected to the ticketRowData variable in my JS, which is being @tracked.

On page load, I query my data (~45,000 rows returned from a BigObject via SOQL, then we do some processing to hack together a quasi "group by" aggregate function (we dont have access to ASYNC SOQL, thus the hack)) via the Wire Service, then it loads the first 100 records into the lightning-datatable.

Originally, I was hoping that updating the ticketRowData JS variable would trigger a rerender of the lightning-datatable, so I tried adding the next 100 rows to the ticketRowData. This gave us a ticketRowData JS variable with 200 rows (tested with Google Dev Tools), so I think I had made the correct change, but the datatable didn't update.

Next I tried zeroing out the original ticketRowData array, and then adding all 200 required rows back into the array. This still didnt reflect any changes in my LWC.

Finally, I was able to populate the table with the correct number of rows, but only after creating one button that zeros out the original array (clearRows), which rerenders the table to have no rows, then creating a second button that populates the table with the appropriate number of rows (addRows), which rerenders the table again, this time having 200 rows.

After I got these promising results, I created a button that would first call the clearRows function, then the addRows function, but once again, there were no changes reflected in the LWC again.

I suppose my ultimate question is: Why isn't my LWC lightning-datatable updating when I make change to its data attribute? And more specifically, why does it only work if I zero out the array and repopulate it in differenct functions?

TL;DR Why does calling my "clearRows" function followed by my "addRows" function from separate lightning buttons re-render my datatable, but calling "clearAndAddRows" doesn't.

Please find below some code snippets of my LWC

componentName.html

<template>
    <lightning-card title="Segment Viewer" icon-name="standard:segments">

         <lightning-button slot="actions" variant="Neutral" label="Clear" title="clear" onclick={clearRows} class="slds-m-left_x-small"></lightning-button>
         <lightning-button slot="actions" variant="Neutral" label="Load 100 More Rows" title="Load 100 More Rows" onclick={AddRows} class="slds-m-left_x-small"></lightning-button>
         <lightning-button slot="actions" variant="Neutral" label="Clear Rows and Add 100 More Rows" title="clear" onclick={clearAndAddRows} class="slds-m-left_x-small"></lightning-button>


    </lightning-card>

    <div class="slds-p-top_small">
         <lightning-layout>
              <lightning-layout-item size={size}>
                   <lightning-card title="Segment Data" style="display:block"> 
                        <div style="height: 500px" class="slds-p-around_small">
                             <lightning-datatable key-field="id"
                                 data={ticketRowData} 
                                 columns={columns}>
                             </lightning-datatable> 
                        </div>
                   </lightning-card>
              </lightning-layout-item>
         </lightning-layout>
    </div>
</template>

componentName.js

import { LightningElement, track, wire, api } from 'lwc';
import getTicketRows from '@salesforce/apex/SegmentController.getTicketRows';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

const columns = [
    { label: 'Account', fieldName: 'AccountId_Text__c' },
    { label: 'Archtics ID', fieldName: 'archticsId' },
    { label: 'Primary Contact', fieldName: 'primaryContact' },
    { label: 'Event Count', fieldName: 'eventCount', type: 'number' },
    { label: 'Seats', fieldName: 'seatCount', type: 'number' },
    { label: 'Dollars Spent', fieldName: 'dollarsCount', type: 'currency', sortable:true },
];


export default class SegmentTool extends LightningElement {
    
    columns = columns;

    accountMap = new Map();
    
    // For Testing, we will set an ID here
    @track userID = '00541000003t5zgAAA';

    @track ticketRowData = [];
    @api totalNumberOfRows;

    tempTicketRowData = [];

    @track error = '';


    @track recordLimit = 100;
    count = 0;


   
    @wire(getTicketRows, {userId : '$userID'})
    wiredTicketRows({
        error,
        data
    }){
        if (data){

            for(var i = 0; i < data.length; i++){
               if(!this.accountMap.has(data[i].AccountId_Text__c)){

                  this.accountMap.set(
                    data[i].AccountId_Text__c, {
                        AccountId_Text__c : data[i].AccountId_Text__c,
                        archticsId : 'test arch Id',
                        primaryContact : 'Pete Testeroni',
                        eventCount: [1],
                        seatCount : [data[i].TM_Number_of_Seats__c],
                        dollarsCount : [data[i].TM_purchase_price__c]
                    })

               } else {
                     this.accountMap.get(data[i].AccountId_Text__c).eventCount.push(1);
                     this.accountMap.get(data[i].AccountId_Text__c).seatCount.push(data[i].TM_Number_of_Seats__c);
                     this.accountMap.get(data[i].AccountId_Text__c).dollarsCount.push(data[i].TM_purchase_price__c);
               }
            }

            this.accountMap.forEach((value, key) => {

                 if(this.count < this.recordLimit){
                      var tempEventCount = value.eventCount.reduce(function (result, item) {
                           return result + item;
                           }, 0);
                      var tempSeatCount = value.seatCount.reduce(function (result, item) {
                           return result + item;
                           }, 0);
                      var tempCashCount = value.dollarsCount.reduce(function (result, item) {
                           return result + item;
                           }, 0);

                      this.tempTicketRowData.push({
                           AccountId_Text__c: value.AccountId_Text__c,
                           archticsId : value.archticsId,
                           primaryContact : value.primaryContact,
                           eventCount : tempEventCount,
                           seatCount : tempSeatCount,
                           dollarsCount : tempCashCount
                      })

                      this.count = this.count + 1;
                }
          });

          this.ticketRowData = this.tempTicketRowData;

        } else if (error){
            this.error = error;
        }  
    }

    clearRows(){
        this.ticketRowData = [];
           
        }

    clearAndAddRows(){
      
        this.ticketRowData = [];
        this.addRows();
       
    }

    addRows(){
        this.recordStart = this.recordLimit;
        this.recordLimit = this.recordLimit + 100;

        let supertempTicketRowData = [];

        supertempTicketRowData = this.tempTicketRowData;

        this.accountMap.forEach((value, key) => {

            if(this.count >= this.recordStart && this.count < this.recordLimit){
                 var tempEventCount = value.eventCount.reduce(function (result, item) {
                     return result + item;
                     }, 0);
                 var tempSeatCount = value.seatCount.reduce(function (result, item) {
                     return result + item;
                     }, 0);
                 var tempCashCount = value.dollarsCount.reduce(function (result, item) {
                     return result + item;
                     }, 0);

                 supertempTicketRowData.push({
                     AccountId_Text__c: value.AccountId_Text__c,
                     archticsId : value.archticsId,
                     primaryContact : value.primaryContact,
                     eventCount : tempEventCount,
                     seatCount : tempSeatCount,
                     dollarsCount : tempCashCount
                 })

                 this.count = this.count + 1;
            }
        });
          this.ticketRowData = supertempTicketRowData;     
    }
}

Edit:
It seems that my error is probably due to array.push() not triggering the component refresh for some reason. For this reason, I tried writing something similar with the ES6 Spread operator, and I was able to get the intended results by replacing the following line of code:

 this.tempTicketRowData.push({
                           AccountId_Text__c: value.AccountId_Text__c,
                           archticsId : value.archticsId,
                           primaryContact : value.primaryContact,
                           eventCount : tempEventCount,
                           seatCount : tempSeatCount,
                           dollarsCount : tempCashCount
                      })

with:

supertempTicketRowData = [...supertempTicketRowData, {
                    id: value.AccountId_Text__c,
                    AccountId_Text__c: value.AccountId_Text__c,
                    archticsId: value.archticsId,
                    primaryContact: value.primaryContact,
                    eventCount: tempEventCount,
                    seatCount: tempSeatCount,
                    dollarsCount: tempCashCount
                }];

Best Answer

My guess is that the table doesn't know to update because you have specified a key field of id but you have not provided on in the object array that you are feeding to the table.

The table likely drives it's refresh algorithm off the key field, so even though you refreshed the array, with all the ids being the same, it won't rerender.

And when you do it in two render cycles - clear -> render -> add data -> render, then this obviously forces a refresh.

So just add this:

this.tempTicketRowData.push({
  id: value.AccountId_Text__c,
  .... all other values

Also you could probably refactor the parser and refresh code to share the same method, maybe with a different param (initial load vs refresh perhaps)

Related Topic