LWC : Tips and recommendations
Since Lightning Web Components (LWC) became generally available in Spring ’19 (around February 2019), many developers have moved from Aura to LWC to keep up with the latest advancement in JavaScript and web standards.
Why?
We have learnt a lot since building our first LWC and this article aims to share some useful tips and recommendations based on our experience on recent Salesforce projects.
For whom?
This article is for all those who have developed or are still actively developing LWC : developer, tech lead, architect.
Recommendations
We have selected a few tips and recommendations when building LWC
connectedCallback v/s getters et setters
Use connected callback when you need to perform initialization tasks, listen to events or to fetch data.
Getter and setter methods are more appropriate to execute custom logic each time a property is set, compared to connectedCallback, which executes only once when the component loads for the first time.
//Gets executed only once connectedCallback() { this.username = this.username.trim(); } //Gets re-evaluated each time the property value changes @api get userName() { return this.username; } set userName(value) { this.username= value.trim(); }
|
Render multiple HTML templates
You can use multiple templates if you have more than one look and feel to display for the component.
In this example, we want to render a page for mobile and a desktop view.
- Create the additional HTML templates in the LWC component folder
- Import all your templates in component.js
import {default as desktopTemplate } from './multipleTemplatesDemo.html' import {default as mobileTemplate } from './multipleTemplatesDemoMobile.html'
- Import formFactor property
import formFactorPropertyName from '@salesforce/client/formFactor';
- Define render method to return the correct template
render() { return formFactorPropertyName === "Small"? mobileTemplate :
desktopTemplate; }
Reuse JavaScript modules in other LWC components
When working in a multi-lingual environment, we need to consider text translation when building LWC.
One of the first things that comes to mind when referring to translation is custom labels.
But what if we need to reference the same or many custom labels in one or many LWC.
Well, one useful tip is to create an “asset” LWC with only the JavaScript and relevant meta XML files and import all required custom labels into this component.
Add all the labels in an object and then export the object.
Notice that you do not need to “export default class … extends LightningElement” because this is a special LWC component not meant to be exposed in any page.
//genGlobalAssets.js import Label1 from '@salesforce/label/c.Label1; import Label2 from '@salesforce/label/c.Label1; import Label3 from '@salesforce/label/c.Label1; //add further labels and include in the object to be exported
const label = { Label1, Label2, Label3 }
export{label};
Then import the “asset” LWC in your working LWC like you would import any other Salesforce module.
//lwcYourComponent.js
import {label} from 'c/genGlobalAssets; export default class LwcYourComponent extends LightningElement {
label1 = label.Label1; //local variable storing label value customFunction() { console.log(this.label1); // access the local variable from any function }
}
The biggest advantage of using this “asset” LWC is the flexibility of adding a new label only once and having it available in the other LWC components that are importing the module.
Uses of wire pageReference
Calling a lightning component from a Lightning App page can cause caching issue, which in turn causes the page to render stale data.
This can be resolved using @wire(CurrentPageReference) and getter/setter methods instead of connectedCallback.
Reference docs :
The connectedCallback() hook is invoked with the initial properties passed to the component. If a component derives its internal state from the properties, it’s better to write the logic in a setter than in connectedCallback()
@wire(CurrentPageReference) captures the updated parameter state each time the page loads, then you can invoke your custom logic based on the new state parameter value.
In this example, let us look at how to navigate to a LWC component embedded in a Lightning App Page using PageReference and eliminate the cache issue.
- Import Navigation Mixin and Current Page Reference in your LWC component
import { NavigationMixin } from 'lightning/navigation'; import { CurrentPageReference } from 'lightning/navigation';
- Extend Navigation Mixin within Lightning Element
export default class TestLWC extends NavigationMixin(LightningElement) {
- Store parameters passed into the Page Reference if needed (here we are storing recordId)
@wire(CurrentPageReference) getStateParameters(currentPageReference) { if (currentPageReference) { this.recordId = currentPageReference.state.recordId; navigateToLWCPage() } }
- Navigate to the LWC component
this[NavigationMixin.Navigate]({ type: 'standard__navItemPage', attributes: { apiName: 'API_Name_App_Page' // api name of the Lightning App Page }, state: { c__yourRecordId : this.recordId //passing parameters to the pageReference } });
HERE’S A FULL SAMPLE CODE USING PAGE REFERENCE
LWC component tab
HTML
<template> <div class="slds-grid slds-theme_default slds-box"> <div class="slds-col slds-size_4-of-12"> <h2 class="slds-text-heading_small slds-var-m-bottom_medium"> Load Id : <code> {loadId}</code> </h2> </div> <div class="slds-col slds-size_8-of-12" if:true={objectApiName}> <lightning-record-view-form object-api-name={objectApiName} record-id={loadId}> <div class="slds-box"> <lightning-output-field field-name="Name"> </lightning-output-field> </div> </lightning-record-view-form> </div> </div> </template>
JS
import { LightningElement,api, wire, track } from 'lwc'; import { CurrentPageReference, NavigationMixin } from 'lightning/navigation'; export default class LwcCustomTab extends NavigationMixin(LightningElement) { currentPageReference; @api loadId; @api objectApiName; @wire(CurrentPageReference) getCurrentPageReference(currentPageReference) { this.currentPageReference = currentPageReference; //show spinner if processing is long new Promise( (resolve, reject) => { setTimeout(() => { if(this.currentPageReference){ this.loadId = this.currentPageReference.state.c__loadId ? this.currentPageReference.state.c__loadId : null; this.objectApiName = this.currentPageReference.state.c__objectApiName ? this.currentPageReference.state.c__objectApiName : null; //perform additional logic with new loadId resolve(); } }, 0); }) .catch((error) => { console.error(JSON.stringify(error)); }) .finally(() => { // disable a spinner if applicable }); } }
XML File
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>53.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__Tab</target> </targets> </LightningComponentBundle>
LWC redirecting to custom tab
HTML
<template> <div class="slds-grid slds-theme_default slds-box"> <div class="slds-col"></div> <div class="slds-col"> <lightning-button label="Redirect To Tab" variant="brand" onclick={navigateToCustomTab} ></lightning-button> </div> </div> </template>
JS
import { LightningElement, api } from 'lwc'; import { NavigationMixin } from 'lightning/navigation'; export default class LwcNavigateToTab extends NavigationMixin(LightningElement) { @api loadId; @api recordId; @api objectApiName; navigateToCustomTab(){ this[NavigationMixin.Navigate]({ type: 'standard__navItemPage', attributes: { apiName: 'LWC_Custom_Tab' }, state: { c__loadId : this.recordId ? this.recordId : this.loadId, c__objectApiName : this.objectApiName } }); } }
XML file
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>53.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__RecordPage</target> <target>lightning__AppPage</target> <target>lightning__HomePage</target> </targets> <targetConfigs> <targetConfig targets="lightning__RecordPage,lightning__AppPage,lightning__HomePage"> <property name="loadId" label="Custom Load Id" type="String" /> <property name="objectApiName" label="Object API Name" type="String" /> </targetConfig> </targetConfigs> </LightningComponentBundle>
Contributing author
Shamina Mossodeea, Senior Manager Comforth Easyfront