I have an LWC which acts as a ToolBox: a user selects a tool from the ToolBelt and then a WorkArea is populated with the business logic for that tool.
Components involved:
Parent: Wrapper.js
Child 1: ToolBelt.js
Child 2: WorkArea.js
Where things are working properly: First, Wrapper.js passes a ToolSelection handler down to ToolBelt.js. On select, an event is emitted from ToolBelt.js to Wrapper.js where the selectedTool variable is being updated.
Where things are not working properly: selectedTool is decorated with @api in the parent, @track in the child, and the value is being successfully updated in the parent. But it is not being rerendered in the child component.
Parent.js:
import { LightningElement, api } from 'lwc';
export default class Toolbox extends LightningElement {
@api selectedTool
@api tools = [redacted]
toolSelectionHandler(event){
let updatedTools
let selectedTool;
const id = event.detail.id
const action = event.detail.action
if( action === 'unselect'){
updatedTools = this.tools.map( (tool) => {
tool.selected = false
return tool
})
this.selectedTool = null
} else{
updatedTools = this.tools.map( (tool) => {
if(tool.id === id){
tool.selected = true
selectedTool = tool
}
else {
tool.selected = false
}
return tool
})
this.selectedTool = selectedTool
}
this.tools = updatedTools
}
}
Parent.html:
<template>
<div class="slds-grid slds-wrap slds-grid--pull-padded">
<div class="slds-p-horizontal--small slds-size--1-of-2 slds-medium-size--1-of-6 slds-large-size--4-of-12" >
<c-toolbelt
tools={tools}
ontoolselected={toolSelectionHandler}
></c-toolbelt>
</div>
<div class="slds-p-horizontal--small slds-size--1-of-2 slds-medium-size--5-of-6 slds-large-size--8-of-12">
<c-work-area
selected-tool={selectedTool}
>
</c-work-area>
</div>
</div>
Leaving out Toolbelt.js and Toolbelt.html because the selection handler is working as expected.
WorkArea.js:
import { LightningElement, track } from 'lwc';
export default class WorkArea extends LightningElement {
@track selectedTool
@track isLoading = false
get tool1(){
let matchBool
if(!this.selectedTool){
matchBool = false
} else {
if (this.selectedTool.title = 'tool1') {
matchBool = true
}
}
return matchBool;
}
get tool2(){
let matchBool
if(!this.selectedTool){
matchBool = false
} else {
if (this.selectedTool.title = 'tool2') {
matchBool = true
}
}
return matchBool;
}
get tool3(){
let matchBool
if(!this.selectedTool){
matchBool = false
} else {
if (this.selectedTool.title = 'tool3') {
matchBool = true
}
}
return matchBool;
}
spinnerHandler(){
this.isLoading = !this.isLoading
}
}
WorkArea.html:
<template>
<div class="work-area">
<div if:true={toolOne} class="tool-detail-area selected">
<c-tool-one
onspinnerhandler={spinnerHandler} >
</c-tool-one>
</div>
<div if:true={toolTwo} class="tool-detail-area selected">
<c-tool-two
onspinnerhandler={spinnerHandler} >
</c-tool-two>
</div>
<div if:true={toolThree} class="tool-detail-area selected">
<c-tool-three
onspinnerhandler={spinnerHandler} >
</c-tool-three>
</div>
<div if:false={selectedTool} class="tool-detail-area default">
<c-no-tool-display></c-no-tool-display>
</div>
<div if:true={isLoading}>
<c-loading-spinner></c-loading-spinner>
</div>
</div>
I've seen a few SO posts about LWC Child components not registering changes made to parent, but most of them are Parent > Child relationship directly. And because events aren't emitted from the child. I haven't seen any where a child modifies state of parent, and tracked variable in sibling isn't re-rendering.
Any help technically or conceptually would be appreciated.
this.selectedTool = JSON.parse(JSON.stringify(this.selectedTool));and see if it helps