2

I have a LWC, which has a number of child LWC, some of which have child LWC on their own etc. Somewhere in a child-child-child... LWC I need a couple of picklists and other data from Salesforce, which I get via @wire, e.g.

import { LightningElement, wire } from 'lwc';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import ACCOUNTSOURCE from '@salesforce/schema/Account.AccountSource';

[...]

accountSources;
    
@wire(getPicklistValues, { recordTypeId: '012000000000000AAA', fieldApiName: ACCOUNTSOURCE })
paymentTermsOptions({err, data}) {
    if(data) {
        this.accountSources = data.values;
    } else if(err) {
        console.error('Can not get account picklist: ' + err);
    }
}

(There are a bunch of these, and some of the data requires logic inside if(data).)

However these picklists and some of the other 'wire data' are needed in the top level component for validation purposes too. What is the right thing to do?

Move all these @wire to the top level LWC and pass the retrieved data through to the child-child-child, making use of a getter like...

get picklists() {
    return {
        picklistA: this.accountSources,
        picklistB: this.picklistB
        [...]
    }
}

Or use the same @wire in grand parent and grand child (and trust the first call is cached) with significant code duplication? (Or is there some way to include the same @wire library on two levels of the hierarchy without code duplication?)

Is there a neater alternative? Something I can access both from parent and child, like a "service component (library)"? (I have tried to move decorators outside of LightningElements and not surprisingly failed.)

1 Answer 1

2

Locker Service has known to cause performance issues, so passing wire data down through multiple layers will cause significant lag versus duplicating code. Lightning Web Security is much better at this, but it's still generally recommended that you avoid multiple levels of data passing when possible, as getters are not always reactive.

As far as using wire methods, remember that they use the Lightning Data Service as a coherent cache, so even though you're "duplicating code," you're not duplicating the number of server-side calls, and it actually ends up being more efficient for every component to just use the same wire method if they need it.

However, if you want to avoid the duplication of code, you could always just use a template component.

Here's some example code.

// template.js
import { LightningElement, wire } from "lwc";
import DemoWire from 'c/wire'; // LWC OSS Example of a wire

export default class Template extends LightningElement {
  @wire(DemoWire) helloData;
}

// child1.js
import Template from 'c/template';

export default class Child1 extends Template {
}

<template>
  <!-- child1.html -->
  Child 1: {helloData.data}
</template>

// child2.js
import Template from 'c/template';

export default class Child2 extends Template {
}

<template>
  <!-- child2.html -->
  Child 2: {helloData.data}
</template>

Demo.

As you can see, by importing a class that extends LightningElement, we don't have to repeatedly specify the same code over and over again. If you specify an .html template in the template component, you can omit it in the child component, and it will automatically inherit the parent's template. Or, you can specify one template per child, as demonstrated in this example. You can even nest components arbitrarily deep if you want to.

The only requirement for wire is that there must be a VM attached to the component. Notably, "service components" don't have a VM attached to them, so you can't use wire in them. However, a parent component will be part of a child's VM, so the wire methods work as you'd expect.

Of course, you could still use a common, shared component. Just create a component that uses wire, and have that component used as common shared data between all components that use it. This is the "orchestrator" design, where one component orchestrates coordination between otherwise disconnected components.

pubsub is an example of this. It uses a subscribe/publish model to notify components of changes anywhere in the hierarchy. Other designs are also possible, depending on your needs.

Finally, if you want to write a service component, you can. Just note that you can't use wire. You can still call the methods imperatively, and then coordinate a way for other components to be notified of new data updates. It's more "manual", but I prefer this design, as it allows me to control when I want my data to be loaded.

2
  • 1
    Many thanks for this inspiring answer! Some of our LWC already "extended" Mixins, which somehow stopped me considering this. But in a test a class Child extends NavigationMixin(Template) worked handsomely now and that's great! I assume "VM" means view model, i.e. the HTML markup? (Which documentation would you recommend in this context?) I neither quite understood the "orchestrator" option - would this basically require to fire an event as soon as @wired received its data? Aug 28, 2022 at 17:49
  • 1
    @FelixvanHove Yes, VM as in View Model. As far as orchestrator, that's the general idea, you can use a pubsub model to notify interested components that new data is available (basically, you're doing the wire's automatic part manually). I don't know of any docs about View Model, the docs tend to not be too technical, but you can review the public parts of lwc's runtime in the repo, which includes a wiring.js that you might be interested in.
    – sfdcfox
    Aug 28, 2022 at 18:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .