Skip to main content

๐Ÿ—️ Salesforce LWC Lifecycle Hooks: The Complete Guide

๐Ÿ’ฌ In plain words: Lifecycle hooks represent the different life stages of a component. constructor = born. connectedCallback = moved into the page. renderedCallback = painted on the screen. disconnectedCallback = removed from the page. Always put your code in the earliest stage that has the data you need to run it.

⚡ 1-Minute Summary (Key Points)

  • Never read @api values in the constructor; they don't exist yet.
  • Use connectedCallback to set up subscriptions and run initial data fetches.
  • renderedCallback fires repeatedly after every single re-render. Always use a boolean guard to prevent infinite loops.
  • Always clean up event listeners and subscriptions inside disconnectedCallback to prevent memory leaks.
๐Ÿ“Œ Example: Setting up a Chart Library

A developer tries to set up a chart library inside connectedCallback. It breaks immediately because the canvas element doesn't exist in the DOM yet. The solution is to move the setup to renderedCallback using a hasRendered boolean guard so it only runs once. Finally, remove the resize listener in disconnectedCallback to clean up.

⏱️ The Exact Order of Operations

When an LWC is inserted into a page, it follows a strict sequence:

  • 1. constructor: Runs first. There is no DOM yet, and no @api values have been passed down from the parent.
  • 2. connectedCallback: The component is inserted into the DOM and parameters are available. This is the perfect place for data subscriptions and imperative data loading.
  • 3. render: Chooses a template (rarely overridden).
  • 4. renderedCallback: The DOM now exists. Warning: It fires after EVERY render, so guard any one-time setup logic.
  • 5. disconnectedCallback: Fires on component removal. Always unsubscribe and clean up listeners here.
  • 6. errorCallback: Catches errors from the child component tree, allowing you to create error boundary components.
๐Ÿง  Born, moved in, painted, moved out: constructor ➔ connectedCallback ➔ renderedCallback (fires often) ➔ disconnectedCallback.

๐Ÿงญ 360 Card — Lifecycle Hooks

๐ŸŽฏ Rule: Born in constructor. In the page at connectedCallback. Painted at renderedCallback. Gone at disconnectedCallback.

๐Ÿš€ Gain: Four clear, distinct moments that provide obvious homes for setup and cleanup logic.

๐Ÿ’ฐ Price: renderedCallback fires after every reactive change. Unguarded work placed here will run repeatedly.

๐Ÿ›‘ Limits: @api values are not available in the constructor. errorCallback catches errors from descendants, but never the parent's own errors.

๐Ÿ”„ Scale: On a console tab, a recordId can change while the component stays mounted. Always use an @wire or an @api setter to handle updates, rather than relying on connectedCallback to refetch.
⚠ INTERVIEW TRAP & EXAM MISTAKE:
renderedCallback fires after EVERY render, not just once. Putting unguarded state mutation inside this hook is the most common cause of the dreaded "infinite render loop" error.

๐Ÿ™‹ Core Q&A & Scenarios

Q: What are the classic LWC lifecycle mistakes and their corrections?
๐ŸŽฏ Say this first: Heavy work in renderedCallback (which fires repeatedly), DOM access in connectedCallback (which is too early), and forgetting to clean up memory in disconnectedCallback.

Detailed breakdown:

  • Mistake 1: Reading @api values in the constructor. They are not set yet. Move it to connectedCallback.
  • Mistake 2: One-time setup in renderedCallback without a boolean guard. The setup will run repeatedly and crash third-party libraries.
  • Mistake 3: Mutating reactive state inside renderedCallback. This triggers another render, causing an infinite loop.
  • Mistake 4: Subscribing to Lightning Message Service (LMS) in connectedCallback without unsubscribing in disconnectedCallback. This causes massive memory leaks and duplicate handlers when switching tabs.
  • Mistake 5: Expecting a child's DOM to be ready inside the parent's connectedCallback. The children are not rendered yet.
Q: How does errorCallback work and what does a production error boundary look like?

A: errorCallback(error, stack) placed on a parent catches errors thrown by its descendant components during their lifecycle or rendering. It does NOT catch the parent's own errors, and it does not catch asynchronous or event-handler errors (those need a standard try/catch).

A production error boundary component wraps a feature region. When it catches an error, it flips to a fallback error template, logs the error stack to a server endpoint via a custom logging service, and offers the user a reset button. Without boundaries, one broken child can silently crash an entire page region.

Q: A component must load data that depends on an @api recordId which can CHANGE while the component remains mounted (like in Salesforce Console apps). Where does the load logic live?

A: Not in connectedCallback. That hook only fires once per mount. If the user clicks a new record in a console app, the recordId changes, but your component will be stuck showing stale data.

Instead, use an @wire that reacts automatically to the '$recordId' parameter, or use an @api setter that fires an imperative fetch when the value actually changes (remember to add an equality check so you don't loop!).

Q: Why not do everything in the constructor?

A: In the constructor, the component is not in the page yet. @api values are undefined, and there is no DOM to manipulate. connectedCallback runs after insertion, making it safe to fetch data. Golden Rule: Talk to absolutely nothing outside the component while inside the constructor.