⚡ Mastering the LWC Lifecycle with a Live-Refreshing Contact Table
In the Salesforce ecosystem, mastering the Lightning Web Component (LWC) lifecycle is more than just knowing when code runs—it's about writing clean, responsive, and performant components that react to data.
This post is a deep dive into how the lifecycle works, from instantiation to render and re-render—featuring a real-world use case: a Contact Table that fetches data via @wire
, renders conditionally with isLoading
, and refreshes automatically when Contact records are added or changed.
🧠 LWC Lifecycle at a Glance
Lifecycle Hooks Used:
-
constructor()
– component is created -
connectedCallback()
– added to DOM -
renderedCallback()
– after DOM rendered -
@wire
– reactively pulls Contact data -
refreshApex()
– re-fetches wire data -
disconnectedCallback()
– cleanup on removal
💻 Code Walkthrough: Contact Table Component
📦 contactTable.js
import { LightningElement, wire, track } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
import { refreshApex } from '@salesforce/apex';
export default class ContactTable extends LightningElement {
@track contacts = [];
@track isLoading = true;
error;
wiredResult;
hasRendered = false;
columns = [
{ label: 'First Name', fieldName: 'FirstName' },
{ label: 'Last Name', fieldName: 'LastName' },
{ label: 'Phone', fieldName: 'Phone' },
{ label: 'Email', fieldName: 'Email' }
];
@wire(getContacts)
wiredContacts(result) {
this.wiredResult = result;
this.isLoading = true;
if (result.data) {
this.contacts = result.data;
this.error = undefined;
} else if (result.error) {
this.contacts = [];
this.error = result.error;
}
this.isLoading = false;
}
renderedCallback() {
if (!this.hasRendered) {
console.log('Rendered!');
this.hasRendered = true;
}
}
refreshTable() {
this.isLoading = true;
refreshApex(this.wiredResult).finally(() => {
this.isLoading = false;
});
}
}
🧩 contactTable.html
<template>
<lightning-card title="Contacts Table">
<template if:true={isLoading}>
<div class="slds-p-around_medium">
<lightning-spinner alternative-text="Loading contacts" size="medium"></lightning-spinner>
</div>
</template>
<template if:true={contacts}>
<lightning-datatable
key-field="Id"
data={contacts}
columns={columns}
hide-checkbox-column="true"
class="slds-p-horizontal_medium">
</lightning-datatable>
</template>
<template if:true={error}>
<div class="slds-text-color_error slds-p-horizontal_medium">
Error loading contact records.
</div>
</template>
</lightning-card>
</template>
🔸 Apex Controller: ContactController.cls
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContacts() {
return [
SELECT Id, FirstName, LastName, Phone, Email
FROM Contact
WHERE Email != null
ORDER BY LastName ASC
LIMIT 50
];
}
}
🔄 Refreshing from the Outside
To allow this table to refresh when a contact is created or updated outside the component (e.g. via Flow, another LWC, or UI action), expose the refreshTable() method:
// parentComponent.js
handleContactChange() {
this.template.querySelector('c-contact-table')?.refreshTable();
}
💡 Pro Tips
- Use @track to ensure reactivity on arrays and loading flags.
- Guard renderedCallback() logic using a hasRendered flag to avoid loops.
- Keep your UI responsive with a well-placed isLoading spinner.
Follow me for more hands-on LWC examples, Flow integrations, and Salesforce development patterns.
Top comments (0)