LWC Lightning Pill Container – Fixing Redraw Issues When Adding Items

With a simple component like so:

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

export default class App extends LightningElement {


    @track infoText;
    @track data = [];

    handleItemRemove(e) {
        this.infoText = e.detail.item.name
    } 

    handleClickW(e) {
        this.data.push({ label: 'a ' + this.data.length, name: 'b' + this.data.length })
        this.redraw = true;
        this.infoText = 'Count:' + this.data.length;
    }
}

HTML:

<template>
    <div class="slds-m-vertical_medium">
        <p>Info: <span class="slds-text-heading_small">{infoText}</span></p>
    </div>
    <div>
        <lightning-button label="Neutral" title="Neutral action" 
        onclick={handleClickW} class="slds-m-left_x-small"></lightning-button>
    </div>


     <lightning-pill-container items={data} onitemremove={handleItemRemove}>
    </lightning-pill-container>

</template>

I never see the items in my {data} displayed in lightning-pill-container. I can get the initial set of items displayed if i wrap lightning-pill-container into a if:true={redraw} and then set it to true on click, but setting it back to false and then to true did not work.

Link in playground:
https://developer.salesforce.com/docs/component-library/tools/playground/v2VK0x6o_/1/edit

How can i add items to the list displayed by lightning-pill-container?

UPDATE

I sort of got it working like so:

@track redraw = false;
    handleClickW(e) {
        this.redraw = false;
        this.data.push({ label: 'a ' + this.data.length, name: 'b' + this.data.length })
        setTimeout(() => { this.redraw = true;}, 0);
        this.infoText = 'Count:' + this.data.length;
    }



<div if:true={redraw}>
     <lightning-pill-container items={data} onitemremove={handleItemRemove}>
    </lightning-pill-container>
    </div>

There are 2 issues:
1) Supposedly setTimeout is not allowed in lwc: Restricted async operation 'setTimeOut' in LWC
2) There is a jitter when i try to add items as the whole thing needs to be re-drawn.

Best Answer

Child components only rerender when its tracked values change. When passing data structures down to child components, we're actually passing by reference, so the reference to the data structure needs to change. In other words, the child component needs to receive a new data structure.

In LWC, data structure mutations can cause components to rerender, but only for the component that owns said data structure. In your example, lightning-pill-container does not update upon mutations to the this.data array because those mutations happen in the c-app component.

I made some modifications to your playground example to illustrate this better: https://developer.salesforce.com/docs/component-library/tools/playground/v2VK0x6o_/2/edit

On a related note, it's a best practice to clone your data structures before passing them around because you don't want other components to mutate your data.

Related Topic