18

How we handle dependent wire in LWC?

For example :

  @wire(getObjectInfo, { objectApiName: OPP_OBJECT })
  objectInfo;

  @wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
  })
  pickListValues;

  @wire(getRecord, { recordId: '$recordId', fields: FIELDS })
  wiredRecord({ error, data }) {
    if (ldsErrorHandling(error) && data) {
      this._status = getFieldValue(data, STATUS_FIELD);
    }
  }

I have an operation that depends on the Stage Field of opportunity and also the picklist values. With normal promises or callbacks I can know when the two values are not null. If is a normal apex call we can have both information at the same time. But how we handle this type of situations with @wire.

My solution for this (is this the only way?):

  @wire(getObjectInfo, { objectApiName: OPP_OBJECT })
  objectInfo;

  @wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
  })
  handlePickValues({ error, data }) {
    if (ldsErrorHandling(error) && data) {
      this._data = data;
      this.dependentLogic();
    }
  }

  @wire(getRecord, { recordId: '$recordId', fields: FIELDS })
  wiredRecord({ error, data }) {
    if (ldsErrorHandling(error) && data) {
      this._status = getFieldValue(data, STATUS_FIELD);
      this.dependentLogic();
    }
  }

  dependentLogic() {
    if (this._status && this._data) //do logic
  }

Thank you in advance

5
  • As far as I can tell you the platform should be able to handle this dependency. Don't know for sure though how. Have you tried this and is there a particular issue that you are facing?
    – Jayant Das
    Apr 26, 2019 at 0:28
  • The issue is, I want to make sure that the two variables are filled before I do my logic. Right now I use in both getPicklistValues and getRecord with functions instead putting directly in a var. Inside the function I call a method. On top of this method I verify if both var are not null, and then I do the logic. I don't like this approach, and I like to know exactly what my code is doing. Apr 26, 2019 at 7:54
  • +1 to @JayantDas's answer: the code you provide should work. If it doesn't please post the minimal code that fails with details of the error. @wire (like Promises) operates asynchronously. You must coordinate their resolution. You can do this with a simple check. Eg if (this.objectInfo.data && this.picklistValue.data) { ... } Jul 18, 2019 at 19:31
  • Hello @KevinVenkiteswaran, the code always worked. My only doubt was that with @wire the only way I know is to call two or more times(depending how many dependencies) the same function and check if all values that we need is not null. And I was asking if has a better way to do. Jul 23, 2019 at 10:09
  • @EliasPinheiro Did you get any solution, what you did at last, I've the same scenario, I don't want to create dependency on the wired services and having a business logic which dependent on two different wired service output. Jan 7, 2020 at 17:23

3 Answers 3

11

According to LWC docs:

The component’s JavaScript prefaces the value of the objectInfo parameter with $ to indicate that it’s dynamic and reactive. It references a property of the component instance. If its value changes, the template rerenders.

Before understanding dynamic and reactive variable lets understand reactive (that is , track).

When track variable is re-assigned/changed in Javascript, LWC will re-render the DOM with updated track variables.

What will happen if dynamic ('$objectInfo') changes?

Well, the wire functions dependent on this variable is triggered.

So, change in dynamic '$objectInfo' will trigger the Javascript functions which are dependent on it. IMPORTANT: this will not re-render DOM.

objectInfo is made both dynamic and reactive by using it as $objectInfo and also decorating it with track.

So, below code will work:

  @wire(getObjectInfo, { objectApiName: OPP_OBJECT })
  objectInfo;

  @wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
  })
  pickListValues;

Reason: Here second wire will be triggered ONLY after first wire updates objectInfo. Thus pickListValues will be updated after getting objectInfo

Best practice: Do not use '$dynamic' variables in HTML (by decorating as track). Because if you are decorating this dynamic variable as track, 1st it will rerender the DOM before wire is triggered and again after wire gets the data, DOM is rerendered again (leading to performance degradation due to twice rerender of DOM). You can verify this with rerenderedCallback.


Important Notes: https://salesforcesas.home.blog/2019/07/23/dynamic-and-reactive-javascript-class-properties-in-lwc-track-wire-and/

6
  • Are you sure that it will trigger that way?
    – ytiq
    Jul 23, 2019 at 13:05
  • YES! Tested it here: salesforcesas.home.blog/2019/07/23/… Jul 23, 2019 at 13:06
  • Hello @salesforcesas, thank you very much for your answer, but I think you did not understand what I asked. I have two wire methods that has no dependencies in each other. But I have business logic that depends on both. And if I made the two wire dependent they will get data in serialized mode that is slower than parallel requests. Jul 23, 2019 at 15:54
  • 1
    If I use getRecord in an imperative way I will lose the reactivity of the wire in the lightning data service. But for other things it will be the best choice for sure. Jul 24, 2019 at 13:33
  • 1
    @EliasPinheiro did you find any way to handle this imperatively without losing reactivity? Jun 3, 2020 at 13:44
4

I had similar requirements. What I decided to do is to have @track property and pass it as a parameter to @wire method.

@track
objectInfo;
@wire(getObjectInfo, { objectApiName: OPP_OBJECT })
setObjectInfo({error, data}) {
    if (data) {
        this.objectInfo = data;
    } else if (error) {
        // ...
    }
}

@wire(getPicklistValues, {
    recordTypeId: '$objectInfo.data.defaultRecordTypeId',
    fieldApiName: STATUS_FIELD
})
pickListValues;

So when this.objectInfo is set, then getPicklistValues is called.

1
  • Hello @ytiq, thank you very much for your answer, but I think you did not understand what I asked. I have two wire methods that has no dependencies in each other. But I have business logic that depends on both. And if I made the two wire dependent they will get data in serialized mode that is slower than parallel requests. Jul 23, 2019 at 15:55
2

Addressing this from the perspective of having logic that is dependent on two wire calls (and not a wire method that depends on another, which has already been answered), an alternative to using a common function call is to put the dependent logic into a common getter:

@wire(getObjectInfo, { objectApiName: OPP_OBJECT })
objectInfo;

@wire(getPicklistValues, {
    recordTypeId: "$objectInfo.data.defaultRecordTypeId",
    fieldApiName: STATUS_FIELD
})
handlePickValues({ error, data }) {
    if (ldsErrorHandling(error) && data) {
        this._data = data;
    }
}

@wire(getRecord, { recordId: "$recordId", fields: FIELDS })
wiredRecord({ error, data }) {
    if (ldsErrorHandling(error) && data) {
        this._status = getFieldValue(data, STATUS_FIELD);
    }
}

get dependentResult() {
    let result;
    if(this._status && this._data) {
        //Process logic based on results from both wires. 
    }
    return result;
}

Then you can use this.dependentResult elsewhere in your component.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.