[SalesForce] How to get an LWC component on a record page to refresh when that page is updated

This is my version of Dana's question:

I have an LWC component that is dropped into a record page (it accepts the recordId property and this is automatically populated by the flexipage infrastructure) that shows data for the record and based on Master Detail children of that object.

I would like to have it automatically refresh itself when the record details are edited through the record page (using the standard "Record Details" component).

[It would be even better if it could refresh when the related list is updated by adding or deleting children through the "Related Lists" standard component]

The component leverages the uiRecordAPI's getRecord service to obtain the field values it needs from the actual record and uses a custom wired Apex method to query the relevant children using the ID returned by getRecord (this ensures the wired method is called only after getRecord has returned the details).

Is it possible to register a listener against some event from the "Record Details" component (and "Related Lists" component?) so I can then use refreshApex to cause the component to re-query details and re-render itself?

UPDATE:

In case this is a "programmer error", here's some bits from the code I have.

Firstly, the component has these properties:

/**
 * The contextual object's record ID. This will identify the Work Schedule
 * instance for which the component is to present and manage the Working Days.
 */
@api recordId;

/**
 * The contextual object's record details needed for presentation purposes.
 *
 * @type {WorkScheduleRecord}
 */
@track record = {
    id: undefined,
    length: undefined,
    cycle: undefined
};

It then has the following wired function to get the record detail it needs when generating its presentation (this uses two imported fields from the schema and has the dynamic recordId passed to it):

/**
 * Fetches the record details so the component has access to the total and cycle length 
 * values. It relies on the ID of the contextual record so this will be triggered ASAP.
 *
 * @param {Object|undefined} error when specified this contains the error(s) encountered 
 * whilst getting the record. May be undefined
 * @param {Object|undefined} data  when specified this contains the record detail. May be 
 * undefined
 */

@wire(getRecord, {recordId: '$recordId', 
                  fields: [TOTAL_LENGTH_FIELD, CYCLE_LENGTH_FIELD]})
receiveRecord({error, data}) {
    if (error) {
        this.error = WorkSchedule.getErrorMessage(error);
    } else if (data) {
        // Populate the tracked record details. Note that the cycle length may be 
        // unspecified in which case it is treated as being set to the same value as the 
        // length. The length must be specified
        this.record = {
            id: this.recordId,
            length: data.fields.namespace__Length__c.value,
            cycle: data.fields.namespace__CycleLength__c.value 
                || data.fields.namespace__Length__c.value
        };
    }
}

Note that this chains to the following wired function call, but only using the ID from the record retrieved:

/**
 * Fetches the working days related to the component's record. This relies on the ID from 
 * the fetched record data, rather than the contextual record ID, to ensure that the total 
 * and cycle lengths are already known before the data is fetched.
 *
 * @param {GetWorkingDaysResponse} response holds either the error(s) encountered whilst 
 * fetching the data or the data itself (or neither in certain cases)
 */
@wire(getWorkingDays, {workScheduleId: "$record.id"})
receiveWorkingDays(response) {
    // Store the response for when the cache needs to be invalidated
    this._workingDays = response;
    let error = response.error;
    let workingDaysByOffset = response.data;

    if (error) {
        this.error = WorkSchedule.getErrorMessage(error);
    } else if (workingDaysByOffset) {
        ... // Lots of processing to set up the visual presentation data
    }
}

If I edit the record to change the Total Length or Cycle Length values this isn't causing getWorkingDays to be invoked. Even if I add the following to the receiveRecord function (after updating this.record) nothing happens:

if (this._workingDays) {
    refreshApex(this._workingDays);
}

If I get this working, it is only half of the solution since I also want to refresh based on addition of new children…

Best Answer

ADDED

wire service getWorkingDays is dependent on record.id. So, this wire will invoke again only when record.id changes. But, although getRecord service is invoked every-time record is updated, its id never changes and so getWorkingDays is never getting invoked again.

You can use imperative apex call and use it inside getRecord function or you can try refreshApex inside getRecord


OLD ANSWER

getRecord from uiRecordApi is based on lightning data services and so it will be invoked automatically when the record is updated.

Sample JS code:

import { LightningElement, wire, api, track } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';

export default class Poc extends LightningElement {
    @api recordId;
    @track account;

    @wire(getRecord, { recordId: '$recordId', fields: [ 'Account.Name', 'Account.Phone' ] })
    getaccountRecord({ data, error }) {
        console.log('accountRecord => ', data, error);
        if (data) {
            this.account = data;
            this.processRelatedObjects();
        } else if (error) {
            console.error('ERROR => ', JSON.stringify(error)); // handle error properly
        }
    }

    processRelatedObjects() {
        console.log('processRelatedObjects for => ', JSON.stringify(this.account));
        // further processing like refreshApex or calling another wire service
    }
}

Proof:

enter image description here


Added based on comments:

Its working even for related list:

enter image description here