0

I'm trying to get the timing of some setup code in an LWC to be reliable. The code needs the opportunity record type Id so it can get the correct picklist values. Then with those picklist values (and some other async data) I'm creating the data structure that my UI needs.

The code I am using is below. While the await for the opportunity record type Id does block, meanwhile the getPicklistValues wire adapter will often have completed for the initial undefined _opportunityRtId value so the next awaitfalls through before the getPicklistValues wire adapter has run again with the correct _opportunityRtId value.

I'm pretty obviously approaching this problem the wrong way. But imperative calls here are not available AFAIK and based on Can we call LWC wire adapters imperatively?. And I get that wire adapters can react to changes in '$...' arguments to offer a form of chaining. And also Chain wire methods together in Lightning Web Components.

Is there a pattern where the code can wait for a chain of @wire calls to settle and then run some code once only? Do you need to set completion flags for each @wire and then when all complete run the code?

_opportunityRtId;

async init() {

    const rtis = await this._opportunityObjectInfo.data.recordTypeInfos;
    this._opportunityRtId = Object.keys(rtis).find(
        rti => rtis[rti].name === '...'
    );

    const results = await Promise.all([
        this._opportunityPicklistValues,
        ...
    ]);

    const r0 = results[0];

    // Initialisation code that uses the picklist values r0.data.values
    // and other async results
}

@wire(getObjectInfo, {
    objectApiName: OPPORTUNITY_OBJECT
})
_opportunityObjectInfo;

@wire(getPicklistValues, {
    recordTypeId: '$_opportunityRtId',
    fieldApiName: OPPORTUNITY_TYPE_FIELD
})
_opportunityPicklistValues;
4
  • not sure if the other code makes this problematic, but are you not able to make the getPicklistValues wire a function wiredPicklistValues({ error, data }) that calls, when data is actually returned, within it whatever init function/logic you need? If you do the same for the object info wire, you'd control when _opportunityRtId gets set and, based on what i'm seeing, have the picklist wire function "last"? Commented Sep 14, 2021 at 17:42
  • The wire service is quite poorly designed. I would use the wire callback "Decorate a function" and after both the callback and your own async data return, call a checkForComplete function that only executes if both pieces of data are present. Note the wire callback is called multiple times making this more painful
    – Andy Ray
    Commented Sep 14, 2021 at 17:49
  • 1
    Hi @KrisGoncalves, Yes I think so; just trying to think the sequence through. The first getPicklistValues call returns the wrong data when _opportunityRtId is undefined, so I can guard my logic by checking that. When _opportunityRtId is set to the right value, getPicklistValues will run again, and this time with _opportunityRtId not undefined I can run my logic. Think that may be it.
    – Keith C
    Commented Sep 14, 2021 at 17:50
  • Thanks @AndyRay. Yeah think I'll give up on the promises and go for the data checks.
    – Keith C
    Commented Sep 14, 2021 at 17:53

1 Answer 1

2

I restructured the code as below and it works reliably now. More painful to construct than is ideal IMHO.

_opportunityRtId = undefined;
_opportunityPicklistValues;
_contactTitles;

@wire(getObjectInfo, {
    objectApiName: OPPORTUNITY_OBJECT
})
wiredOpportunityInfo({ error, data }) {
    if (data) {
        const rtis = data.recordTypeInfos;
        const rtId = Object.keys(rtis).find(rti => rtis[rti].name === '...');
        this._opportunityRtId = rtId;
    } else if (error) {
        this.error('getObjectInfo error ' + JSON.stringify(error));
    }
}

// Reacts to record type Id
@wire(getPicklistValues, {
    recordTypeId: '$_opportunityRtId',
    fieldApiName: OPPORTUNITY_TYPE_FIELD
})
wiredOpportunityTypePicklist({ error, data }) {
    if (data) {
        // Only interested in the results when the record type Id is defined
        if (this._opportunityRtId) {
            this._opportunityPicklistValues = data.values;
            this._init();
        }
    } else if (error) {
        this.error('getPicklistValues error ' + JSON.stringify(error));
    }
}

async _init() {

    // Imperative data
    if (!this._contactTitles) this._contactTitles = await queryContactTitles();

    // Wired data
    if (!this._opportunityPicklistValues) return;

    // Rest of init logic
    ...
}

PS

In Understand the Wire Service there is:

Properties in the adapterConfig object can’t be undefined. If a property is undefined, the wire service doesn’t provision data.

so it does look like leaving properties undefined should stop the wire service from running before you want it to.

You must log in to answer this question.

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