[SalesForce] LWC: Refresh for:each loops when new Array item added

I have 2 LWC components and loop through data in the following structure
periods
period.stages
stage.details

Within stage.details I render a table/form for each detail. After the table/form I have an add new row button. The markup is similar to the following pseudocode

<template>
    <template if:true={periods}>
        <template for:each={periods} for:item="period">
            <template if:true={period.stages}>
                <template for:each={period.stages} for:item="stage">
                    <c-innercomponent stage={stage} 
                         The markup within c-innercomponent...
                        <template for:each={stage.details} for:item="detail">
                            A table row is added for each
                        </template>
                         <lightning-button/>  Add-new row button 

This all renders well and I can render my table and my add-new button. On clicking Add-new I add a new row/element to the current stage.details and send it back to the Outercomponent-1 via an event.

On OuterComponent: after receiving the event I reassign (for the correct stage) period.stages.stage.details to the new version.
in the console.log I print out this.periods and it all looks good however the UI does not update.

Should this update automatically? I've imported "track" from LWC however I don't @track periods as @track'ing attributes are no longer required.

I tried to use a rebuild array on rebuilding my this.periods via
this.periods = […this.periods, rebuild]; by following the accepted answer in Array.push() won't update Lightning Web Component View but it does not update the UI.

Does anyone know what I may need to do to allow the UI to reflect the changed data?

Note, no data is DML'd at any stage to the database therefore I was thinking @wire would not be a candidate to help.

Any tips would be appreciated. Thanks in advance for your help.

Best Answer

Should this update automatically? I've imported "track" from LWC however I don't @track periods as @track'ing attributes are no longer required.

It's no longer necessary for simple objects. For example:

checked = true;

You don't need to @track this, because any change will cause a render. However, once you have a complex object:

settings = { checked: true }

You now need to @track the variable in order to trigger a render (or, copy the entire object).

In other words:

this.settings.checked = false; // will not trigger render if you do not @track
this.settings = { ...this.settings, checked: true }; // copy triggers render

It's useful to use @track if you don't want to have to copy objects. Note that deeply nested objects have to be copied all the way down to trigger render changes, while @track simply does this for you automagically.

The ... operator doesn't do a deep copy, only a "shallow" copy, so when you copied the array, the elements inside were not also copied. There's polyfill methods out there you can find for recursive deep copy, but that's usually overkill when a simple @track annotation will achieve the same desired result.

Related Topic