18

I know that @wire methods are always executed once our component is loaded. I want to dig deep into this and want to understand the order of execution with respect to the component's life cycle. When it is actually that the @wire method will be invoked. Is it after constructor(), before renderedcallback()?
If that is the case then is it not always beneficial to get data using Imperative apex(from the constructor()) to improve performance(loading of component) instead of @wire?

1
  • NB: wires that take dynamic parameters are called once the parameters have defined values/values change. This could be at any point in your component's active lifecycle.
    – Phil W
    Commented May 29, 2022 at 13:19

2 Answers 2

32

In a typical wire setup, the life cycle will be:

  • constructor
  • wire provisions empty object ({data, error} are each undefined)
  • connectedCallback
  • render
  • renderedCallback
  • wire provisions results from server
  • render (if dirty data)
  • renderedCallback (ditto)

Using imperative Apex in the constructor, you end up with:

  • constructor
  • connectedCallback
  • render
  • renderedCallback
  • Apex call returns data
  • render (if dirty data)
  • renderedCallback (ditto)

In other words, you'll have at least two render cycles either way. Combining the two ends up with up to 3 render cycles (initial, Apex, wire, in that order).

With either scenario, any server-side code will be delayed until after the first renderedCallback. The initialization process of components is atomic (here, meaning a single indivisible unit of execution). All asynchronous actions (both wire and Apex), up to the allowed limit of 2,500 actions, will be queued up and sent to the server in a single call.

As such, there is no inherent benefit in trying to call Apex from the constructor. If you want your component to load an initial state faster (e.g., so you can show a "loading template"), you may want to actually use connectedCallback with imperative Apex:

async connectedCallback() {
  await Promise.resolve();
  let apexResults = await Promise.all([ method1(params), method2(params) ])
  // Do something with results
}

The await Promise.resolve() allows all of the wire methods to go first, in a separate server roundtrip, then the Apex methods will be called afterwards in a second roundtrip. This trades overall load time for a faster initial render. If you have really "heavy" components, using a small delay with this method should give a better impression that the page is loading faster than it actually is.

Also note that wire handlers won't trigger with reactive parameters when those reactive parameters are still undefined. Consequently, if one wire handler depends on another, there may be multiple render/renderedCallback cycles in a cascading fashion. However, this can also break up the rendering cycle to give a more responsive UI.

5
  • I do think you should mention the delay of wire invocation until dynamic parameters have defined values.
    – Phil W
    Commented May 29, 2022 at 18:28
  • @PhilW You're not wrong. I thought it was insignificant wrt "initial loading", but you're right, it should be included.
    – sfdcfox
    Commented May 29, 2022 at 21:46
  • You also state all calls are queued up and sent as a single call. I thought that is only true when you exceed the boxcarring threshold of 5 though honestly haven't investigated behaviour on single component initialization so would be interested for you to explicitly confirm.
    – Phil W
    Commented May 30, 2022 at 6:06
  • @PhilW See this. Of course, it could change in the future, but the point is, for now, anything you call before the first renderedCallback will likely go at once, even bypassing the typical "boxcar" effect. Limiting your code to call as few methods as possible will improve loading performance.
    – sfdcfox
    Commented May 30, 2022 at 6:15
  • Ah, fair enough - always good to learn new things 😀. Yes, of course, this is all undocumented behaviour quite intentionally, so it is important not to rely on such behaviours. That said, boxcarring itself has been acknowledged by Salesforce, and in separate discussions they have said it is something they would consider providing some control over (since it really can detrimentally impact user experience).
    – Phil W
    Commented May 30, 2022 at 6:48
1

Apologies for posting a new answer - my reputation is insufficient to comment on @sfdcfox's answer above. This question came up in an internal discussion and as one of folks on the team that owns the Apex adapter in question I wanted to add a few notes to @sfdcfox's very thorough answer above:

  1. Make your call as soon as (a) you have all the config you need to pass, and (b) are prepared to handle the results. @wire will do this automatically; for imperative calls it's up to you.
  2. Don't make assumptions about when the data will return. There are potentially multiple layers of caching involved in your request that can radically change how quickly you get the data. Lightning Data Service has effectively no notion of component lifecycle and always tries to return data as quickly as possible. There are LWC layers between Lightning Data Service and your component that can also impact when you see data on your @wire.
  3. Don't make assumptions about how often your @wire will receive data. Many component authors treat @wire as if it were an RPC to the server. It's not, and thinking of it that way can get you into trouble. Your best bet is to think of @wire as a way for your component to tell the framework what data it needs to build your UI. The framework owns figuring out when, how, how often, and from where to get that data.
  4. Be very careful optimizing your component for current Salesforce platform behavior. I understand you want your UI to be as fast as you can make it (we want that, too!) but the framework understands lots more about what is happening on the page than your component does. We are constantly evolving and optimizing the framework, and approaches that make your code faster today might make it slower tomorrow.

You must log in to answer this question.

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