DEV Community

Cover image for Mastering the LWC Lifecycle with a Live-Refreshing Contact Table
Vladimir Gorelik
Vladimir Gorelik

Posted on

Mastering the LWC Lifecycle with a Live-Refreshing Contact Table

⚡ 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:

  1. constructor() – component is created
  2. connectedCallback() – added to DOM
  3. renderedCallback() – after DOM rendered
  4. @wire – reactively pulls Contact data
  5. refreshApex() – re-fetches wire data
  6. 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;
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

🧩 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>
Enter fullscreen mode Exit fullscreen mode

🔸 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
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

🔄 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();
}
Enter fullscreen mode Exit fullscreen mode

💡 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)