I've seen many examples with primitive data types, but I've been trying to propagate array down to my children component and encountered some problems.
My parent component looks like this:
<template>
<lightning-card title="ListReference">
<div class="slds-p-around--medium">List: {listString}</div>
<c-list-reference-inner
list={list}
></c-list-reference-inner>
</lightning-card>
</template>
and its javascript:
import {LightningElement, track, api} from 'lwc';
export default class ListReference extends LightningElement {
@track list = [{},{},{}];
constructor() {
super();
setInterval(() => {
this.list = [...this.list];
}, 3000);
}
get listString() {
return JSON.stringify(this.list);
}
}
This is how my child component listReferenceInner looks like:
<template>
<lightning-button
variant="brand"
label="mutate list"
class="slds-m-left_x-small"
title="mutate"
onclick={mutateListElement}
></lightning-button>
</template>
and javascript:
import {LightningElement, api} from 'lwc';
export default class ListReferenceInner extends LightningElement {
@api list;
mutateListElement() {
this.list.forEach(element => element.ttt = 'ttt');
}
}
What happens is that when the button on the child component is clicked,
the array's elements are updated. I also put setInterval to the parent component's constructor, which just clones the array so the @track catches this change and rerender the component so we can see that the array changes were propagated up from the child component. So far I know this isn't something we want, because LWC uses one-way binding.
Moreover, if I change the decorator on the parent component from @track to @api, the child function mutateListElement
quietly dies on the first element.ttt = 'ttt'
, maybe because of some trap on the element's proxy.
I believe I could solve this with a setter on the child list which would make a copy of that array, but I find that ugly. So my question is how to treat object references when propagated like this?
Thanks for any answers!
Best Answer
The documentation on this topic emphasizes that any child component should treat the value passed from parent as read-only. If you try to mutate the data passed from parent to child, it results in an error (emphasis mine).
As for your question:
That's in fact the recommended way to pass data from parent to child is:
So its very likely that what you are observing is true. But, if you really don't want to change data in child, then you should not be attempting to pass the data as reference in first place. As a result, your only option to address the issue is to make sure that you pass a copy by setting a property or calling a function on the child component.