0

Consider a simple LWC component that has searchRadius as a property:

    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <property name="searchRadius" type="Integer" label="Search Radius" default="30"/>
        </targetConfig>
    </targetConfigs>

The component itself:

export default class Sample extends LightningElement {
    @api recordId;
    @api searchRadius;

    @wire(getRecord, { recordId: '$recordId' })
    async wiredRecord({ error, data }) {
        if (data) {
            const accounts = await fetchAccounts({
                recordId: data.fields.Id.value,
                searchRadius: this.searchRadius
            });
        } else if (error) {
            console.error('Error fetching data:', error);
        }
    }
}

And the Apex method for the component:

    @AuraEnabled(Cacheable=true)
    public static List<Account> fetchAccounts(Id recordId, Integer searchRadius) {
        if (searchRadius == null) return new List<Account>();

        return [SELECT Id FROM Account];
    }

Notice how I check for null in fetchAccounts() first - if I don't do that I get an error. This is because fetchAccounts() gets called multiple times - the first time without a radius. Now this is pretty ugly already and it gets worse the more parameters I add.

I would guess that I'm missing something fundamental here. Like, when a wired method gets called and when data from the properties is actually loaded. Can someone point me in the right direction?

2

2 Answers 2

2

Note this answer to your previous question around how wires get that "dummy" call.

Wires are called once all reactive parameters have values that are non-undefined (so null is a value that will cause a wire to invoke). If you store the result of getting the record in properties of your LWC that are themselves used in a wire-based call to fetchAccounts, you won't need to worry about sequencing; it will all get resolved for you automatically.

You could do something like:

export default class Sample extends LightningElement {
    @api recordId;
    @api searchRadius;
    accountId;

    @wire(getRecord, { recordId: '$recordId' })
    wiredRecord({ error, data }) {
        if (data) {
            // Simply update the reactive parameter for fetchAccounts
            this.accountId = data.fields.Id.value;
        } else if (error) {
            console.error('Error fetching data:', error);
        }
    }

    // Converted from imperative to wire invocation
    @wire(fetchAccounts, { recordId: '$accountId', searchRadius: '$searchRadius' })
    wiredAccounts({ error, data }) {
        if (data) {
            const accounts = data;

            // ...
        } else if (error) {
            // ...
        }
    }
}

BTW: In this case, use of data.fields.Id.value is a bit pointless, since this will be the same as this.recordId anyway, and you could remove the whole getRecord call and swap accountId for recordId in the call to fetchAccounts. However, if you've got a reason for this that you simply omitted from the question, fair enough.

2
  • Note that recordId == accountId, so the getRecord step is redundant. Of course, this is only an example, there may be other reasons for calling two different methods, but in theory, using the same reactive parameter would be beneficial to performance.
    – sfdcfox
    Commented Mar 28 at 14:01
  • Yeah, I commented on that a little after the fact, but there may be some unrevealed reason... who knows?!
    – Phil W
    Commented Mar 28 at 14:03
1

Under LWR conditions, the order would match what I describe in Order of execution of @wire in lwc. However, there's an extra wrinkle thrown in. In the One App (Lightning Experience), components can be cached and reused for performance reasons. Because of this design, wire methods may be triggered multiple times, as you've observed, and parameters may be populated in a different order than the default.

At first glance, I'd suggest you'd not call the data imperatively, since you can just:

@wire(fetchAccounts, { recordId: '$recordId', searchRadius: '$searchRadius' }) nearbyAccounts;

Wire will wait until the parameters are populated before calling the Apex code. Even better, both methods will theoretically be called in a single round-trip to the server, improving overall performance. You can use a handler for each, if you need to.

Alternatively, wait until you have the search radius, saving yourself a round-trip to the server and simplifying your code:

@wire(getRecord, { recordId: '$recordId' })
async wiredRecord({ error, data }) {
    if (data && this.searchRadius) {
        const accounts = await fetchAccounts({
            recordId: data.fields.Id.value,
            searchRadius: this.searchRadius
        });
    } else if (error) {
        console.error('Error fetching data:', error);
    }
}

This should take care of the situation where your component is being reused, and consequently having the parameters somehow not populated/incorrect.

You must log in to answer this question.

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