Skip to main content

๐Ÿš€ Salesforce LWC Data: Wire Caching, RefreshApex & Lazy Loading

๐Ÿ’ฌ In plain words: When you use @wire in Lightning Web Components, Salesforce caches the results. If a component asks for the same data twice, it only costs one server trip. That gives you free speed, but it creates a risk of stale data. When you change data, you must manually call refreshApex to force a fresh copy. Lazy loading means you don't fetch or render heavy components until the user actually scrolls down to see them.
๐Ÿ“Œ Example: Handling Stale Cache Data

Screen A uses @wire to fetch getOpenCases. A user opens Screen B, edits a case, and returns to Screen A. The list on Screen A is now stale, because it is being served from the local wire cache rather than the server. To fix this, store the entire wired result object (wiredCases) and call refreshApex(this.wiredCases) immediately after the edit in Screen B completes. The cache gives you speed; the refresh gives you the truth.

๐ŸŽฌ Real-Life Example: The List That Would Not Refresh

❌ The Old/Bad Way: A logistics dispatcher screen shows open deliveries using a wired Apex method. When a dispatcher reassigns a delivery, the list still shows the old driver. To "fix" this, developers added a JavaScript setInterval timer that called the Apex method every ten seconds. This burned massive server limits by polling constantly and still showed stale data for up to ten seconds.
✅ The New/Good Way: The developer stores the entire wired result object. When the save action resolves, they call refreshApex on that stored result. This forces one targeted refresh at the exact moment the data changes, allowing the cache to do its job the rest of the time without burning server limits.

๐Ÿง  The Core Concepts: Cacheable Data & LWC

Data flowing through an @wire decorator, whether from a UI API adapter (like getRecord) or an @AuraEnabled(cacheable=true) Apex method, goes straight through the Lightning Data Service (LDS) client-side cache.

  • Shared Cache: Repeated identical requests are served locally. If twelve different components on a page wire the same record, Salesforce only makes one trip to the server.
  • Cacheable Rules: Marking an Apex method as cacheable=true forbids any Data Manipulation Language (DML) like insert or update. It must be a pure read.
  • Forcing a Refresh: Use refreshApex(wiredResult) to force a re-provision of a cached Apex method after you make imperative changes. Use notifyRecordUpdateAvailable(recordIds) if you imperatively update a record that is being monitored by standard LDS adapters.
  • Lazy Loading: Refers to both data (pagination, load-more scrolling) and code (using dynamic imports to only load modules when a user interacts with them).
๐Ÿ› ️ Golden Rule: Reads are Cacheable and Wired. Writes are Imperative. refreshApex is the bridge that links them together.

๐Ÿงญ 360 Card — Wire Caching & Lazy Loading

๐ŸŽฏ Rule: Because @wire requests are cached, multiple components asking for the same data only cost one server trip. You must refresh the cache manually when you change data.

๐Ÿš€ Gain: Massive performance boosts for the end user and dramatic reductions in server load/API calls.

๐Ÿ› ️ Reach for: Let the cache do the work first. Reach for refreshApex only after YOU change the data. Reach for lazy loading only when a page has more than a handful of components or heavily nested data.

๐Ÿ’ฐ Price: Staleness. The local cache cannot see writes made by other users, background integrations, or your own imperative Apex methods unless you tell it.

๐Ÿ›‘ Limits: A cacheable Apex method will throw an exception if you try to put DML inside it. Also, refreshApex requires you to pass the entire wired result object, not just the .data property inside it.

๐Ÿ™‹ Core Q&A & Scenarios

Q: When must a cacheable wire be refreshed manually, and what are the exact mechanisms to do it?
๐ŸŽฏ Say this first: You must refresh the wire when data changes outside of what the Lightning Data Service (LDS) can see. Use refreshApex(wiredResult) for Apex, and notifyRecordUpdateAvailable(recordIds) for LDS standard records.

Detailed breakdown:

  • The cache goes stale if you run imperative Apex DML, if another user edits the record, or if a backend integration writes to the record. The local cache is blind to all of these.
  • To fix it: Pass the full provisioned object to refreshApex(this.wiredValue).
  • If you imperatively touched standard records that the UI API serves, use notifyRecordUpdateAvailable(recordIds) to tell LDS to fetch a fresh copy.
  • If you need to instantly know about another user's changes, you must use Change Data Capture (CDC) or empApi subscriptions.
Q: Why does an @AuraEnabled(cacheable=true) method throw an error when you add an insert statement? How do you fix the design?

A: A cacheable method must be a pure read. Salesforce enforces this rule strictly: no DML is allowed. Think about it—if the method was cached, and the user clicked "Save" a second time, the cache would return a hit, skip the server trip, and the database insert would never happen.

The Fix (CQRS pattern): Split your commands from your queries. Write a non-cacheable imperative method to perform the DML mutation. Once that promise resolves, call refreshApex on your separate, cacheable @wire method to fetch the newly updated list.

Q: A Lightning record page with 12 custom components loads incredibly slowly. What is the triage process for lazy-loading and caching?

A: Always measure with browser profiling tools first. Then, work in this exact order:

  • 1. Dedupe: Ensure those 12 components aren't independently calling identical imperative Apex. If they all use @wire(getRecord), they will share the LDS cache and reduce 12 round trips down to 1.
  • 2. Defer (Lazy Load): Push below-the-fold components into Lightning App Builder Accordions or Tabs. Inactive tabs do not mount their components until clicked.
  • 3. Dynamic Imports: Defer loading massive third-party JavaScript libraries using dynamic imports until the exact moment they are needed.
  • 4. Paginate: Implement "load-more" scrolling so you only fetch 50 rows of data at a time.
  • 5. Check Loops: Verify you don't have infinite render loops happening inside renderedCallback masquerading as a slow network load.
⚠ Developer Note: Wire Cache vs. Platform Cache
While both are caches, they live on entirely different floors of the architecture. The @wire / LDS cache lives in the user's browser, lasts only for their session, and is managed automatically by Salesforce. The Platform Cache lives on the Salesforce servers, is shared across all users, and is managed manually by your Apex code. They are not interchangeable concepts.