Top 10 Tips for Optimizing Lightning Web Component Development

Salesforce Lightning Web Components (LWC) is a modern, lightweight framework for building web applications on the Salesforce platform. LWC provides a set of reusable, high-performance web components that can be easily integrated into any Salesforce application. To get the most out of LWC, it’s important to follow best practices to ensure that your components are efficient, maintainable, and easy to use.

1️⃣ Use the @api decorator for properties

The @api decorator is used to define public properties in a Lightning web component. These properties can be accessed and modified by other components and by Apex controllers. When using the @api decorator, it’s important to always provide a default value for the property, so that it has a defined state even if it’s never set.

Here is an example of a simple LWC component that uses the @api decorator to define a public property:

<template>
<div>
<button onclick={toggleVisibility}>Toggle</button>
<template if:true={isVisible}>
<div>This element is visible</div>
</template>
</div>
</template>
<script>
import { LightningElement, track } from 'lwc';
export default class VisibilityToggle extends LightningElement {
@track isVisible = false;
toggleVisibility() {
this.isVisible = !this.isVisible;
}
}
</script>

2️⃣ Use the @track decorator for properties

The @track decorator is used to define properties that should be tracked by the framework for changes. Before Spring ’20, to make the component rerender when a user entered a first or last name, you had to decorate the fields with @track

@track firstName = '';
@track lastName = '';

Fields are reactive by default now. Expandos, which are properties added to an object at runtime, aren’t reactive.

Reactivity Considerations

Despite the fact that fields are reactive, the LWC engine only tracks field value changes in a shallow manner. When a new value is assigned to a field, changes are detected by comparing the value identity using ===. This is useful for primitive types such as numbers and booleans.

import { LightningElement } from 'lwc';
export default class ReactivityExample extends LightningElement {
bool = true;
number = 42;
obj = { name: 'John' };
checkMutation() {
this.bool = false; // Mutation detected
this.number = 42; // No mutation detected: previous value is equal to the newly assigned value
this.number = 43; // Mutation detected
this.obj.name = 'Bob'; // No mutation detect: `obj` field value is not reassigned
this.obj = { name: 'John' }; // Mutation detected - redefining the object with the same value creates a new object
this.obj = { ...this.obj, title: 'CEO' } // Mutation detected
}
}

When working with complex types such as objects and arrays, you must first create a new object and assign it to the field in order for the change to be detected.

To avoid such issues when working with complex objects, use the @track decorator to deeply track field value mutations.

Track Changes Inside Objects and Arrays

To observe changes to the properties of an object or to the elements of an array, decorate the field with @track.

When a field is decorated with @track, Lightning Web Components tracks changes to the internal values of:

  • Plain objects created with {}
  • Arrays created with []

The framework observes mutations made to plain objects and arrays in a recursive fashion, including nested objects, nested arrays, and a mix of objects and arrays. Cyclic references are also handled.

However, the framework doesn’t observe mutations made to complex objects, such as objects inheriting from Object, class instances, DateSet, or Map

3️⃣ Use the @wire decorator to load data

Use the @wire decorator to retrieve data from Salesforce. This will help keep your component’s data in sync with the server.

The @wire decorator is used to retrieve data from Salesforce and keep it in sync with a component’s properties. It makes it easy to retrieve data in a performant and efficient way, without having to manually handle the data retrieval and updates.

Here is an example of how to use the @wire decorator to retrieve data from Salesforce.

import { wire,api } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
export default class MyComponent extends LightningElement {
@api recordId;
account;
@wire(getRecord, { recordId: '$recordId', fields: ['Account.Name', 'Account.Industry'] })
wiredAccount({ data, error }) {
if (data) {
this.account = data;
} else if (error) {
console.error(error);
}
}
}
view raw wireAdapter.js hosted with ❤ by GitHub

In this example, the getRecord method from the lightning/uiRecordApi module is imported and used to retrieve an account record by its Id. The recordId property is decorated with the @api decorator and passed as a parameter to the getRecord method. The fields to be retrieved are also passed as an argument. The wiredAccount method is decorated with the @wire decorator and takes the form of a callback function that is called with an object containing the data and error properties. If the data is present, it is stored in the account property and if the error is present it is logged in the console.

4️⃣ Event handling to communicate between components.

LWC components can communicate with each other using custom events. A component can fire an event to notify other components that something has happened, and other components can listen to that event and respond accordingly.

Here is an example of a LWC component that fires an event when a button is clicked:

<template>
<button onclick={handleClick}>Click me</button>
</template>
<script>
import { LightningElement } from 'lwc';
export default class EventFiringComponent extends LightningElement {
handleClick() {
const event = new CustomEvent('myevent', { detail: { message: 'Hello World' } });
this.dispatchEvent(event);
}
}
</script>

And another component that listens for that event and responds accordingly:

<template>
<div>{message}</div>
</template>
<script>
import { LightningElement } from 'lwc';
export default class EventHandlingComponent extends LightningElement {
message;
connectedCallback() {
this.addEventListener('myevent', this.handleEvent);
}
handleEvent(event) {
this.message = event.detail.message;
}
}
</script>

5️⃣ Use the Lightning Data Service (LDS) to handle CRUD operations.

The Lightning Data Service (LDS) is a set of tools provided by Salesforce that allows you to easily handle CRUD operations within your LWCs. It provides a powerful, easy-to-use API for working with Salesforce data, and can significantly simplify your code.

Here is an example of how to use LDS to create a new record:

import { LightningElement, api } from 'lwc';
import { createRecord } from 'lightning/uiRecordApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
export default class MyComponent extends LightningElement {
@api objectApiName;
@api recordId;
handleCreateRecord() {
const fields = {
Name: 'Test Account'
};
const recordInput = { apiName: this.objectApiName, fields };
createRecord(recordInput)
.then(response => {
this.recordId = response.id;
})
.catch(error => {
console.error(error);
});
}
}
view raw ldsExample.js hosted with ❤ by GitHub

In this example, the createRecord method from the lightning/uiRecordApi module is imported and used to create a new account record with a Name field set to ‘Test Account’. The response from the createRecord method includes the id of the newly created record, which is then stored in the recordId property.

6️⃣ Use the Lightning Message Service (LMS) to communicate between components.

The Lightning Message Service (LMS) is a powerful tool provided by Salesforce that allows you to communicate between different components in your application. It provides a simple, easy-to-use API for sending and receiving messages, and can be used to build complex, event-driven applications.

Here is an example of how to use LMS to send a message:

import { LightningElement } from 'lwc';
import { MessageContext, publish } from 'lightning/messageService';
import ACCOUNT_SELECTED from './accountSelected.js';
export default class MyComponent extends LightningElement {
@api recordId;
handleAccountSelect() {
const message = { recordId: this.recordId };
publish(this.messageContext, ACCOUNT_SELECTED, message);
}
connectedCallback() {
this.messageContext = MessageContext.getInstance();
}
}

In this example, the publish method from the lightning/messageService module is imported and used to send a message containing the recordId of the selected account. The message is sent with a type of ACCOUNT_SELECTED, which is imported from a separate file. The handleAccountSelect method is called when the user selects an account, it creates a message object with the recordId and then sends it with the publish method. The connectedCallback method is used to get the instance of the message context and store it in the messageContext property. This is required to be able to send a message via the publish method.

It’s worth noting that in order to receive the message, the component that needs to receive the message needs to subscribe to the same message channel and import the message type.

import { LightningElement, track } from 'lwc';
import { MessageContext, subscribe } from 'lightning/messageService';
import ACCOUNT_SELECTED from './accountSelected.js';
export default class ReceiverComponent extends LightningElement {
selectedRecordId;
selectedRecord;
connectedCallback() {
subscribe(this.messageContext, ACCOUNT_SELECTED, this.handleSelectedAccount, { scope: APPLICATION_SCOPE });
this.messageContext = MessageContext.getInstance();
}
handleSelectedAccount(message) {
this.selectedRecordId = message.recordId;
//fetch selected record with the id
}
}

In this example, the component is subscribing to the ACCOUNT_SELECTED message channel by calling the subscribe method in the connectedCallback method. The component is passing a callback function handleSelectedAccount which will be called when a message of type ACCOUNT_SELECTED is received and passing a scope of the message (APPLICATION_SCOPE). The callback function receives the message and sets the selectedRecordId property.

It’s important to note that the component should be subscribed to the message channel before it starts publishing the message.

7️⃣ Use the @wire decorator with the config property to handle errors.

The config property can be used in conjunction with the @wire decorator to handle errors that may occur during a wire call. It allows you to specify a function that will be called when an error occurs, making it easy to handle and display error messages to the user.

Here is an example of how to use the config property to handle errors:

import { wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
export default class MyComponent extends LightningElement {
@api recordId;
@track error;
@wire(getRecord, { recordId: '$recordId', fields: ['Account.Name'] }, {
error: this.handleError
})
account;
handleError(error) {
this.error = error;
}
}

In this example, the handleError function is passed as the error property of the config object. This function will be called whenever an error occurs during the wire call, allowing you to handle and display the error message to the user.

8️⃣ Use the connectedCallback() lifecycle method to perform initialization tasks.

The connectedCallback() method is a lifecycle method that is called when a component is inserted into the DOM. It can be used to perform initialization tasks such as fetching data or setting up event listeners.

Here is an example of how to use the connectedCallback() method:

export default class MyComponent extends LightningElement {
@api recordId;
connectedCallback() {
this.fetchData();
}
fetchData() {
// Fetch data from the server
}
}

In this example, the fetchData method is called when the component is inserted into the DOM via the connectedCallback method. This allows the component to fetch the necessary data from the server as soon as it is added to the page.

9️⃣ Use the disconnectedCallback() lifecycle method to perform cleanup tasks.

The disconnectedCallback() method is a lifecycle method that is called when a component is removed from the DOM. It can be used to perform cleanup tasks such as removing event listeners or canceling network

🔟 Use best practices for performance

  • Use the track decorator for properties that are used to control the component’s state or behavior, and use the api decorator for properties that are accessed by other components or Apex controllers.
  • Avoid using the setTimeout() function in your LWC. Instead, use the setImmediate() function, which allows you to schedule work to be done as soon as the browser is able.
  • Use the @track decorator only for properties that are used in the component’s template, and avoid using it for properties that are used only in JavaScript code.
  • Avoid using the for:each directive with large collections of data. This can cause the component to re-render multiple times, which can impact performance.

By following these best practices, you can ensure that your LWC components are efficient, maintainable, and easy to use. Remember that LWC is a powerful framework that allows you to build web applications quickly and easily, but it’s important to understand its capabilities and limitations in order to get the most out of it.

Want to stay up-to-date on the latest tips and tricks for building high-performing Lightning Web Components? Be sure to subscribe to our blog for the latest best practices, code examples, and expert insights on how to optimize your LWC development.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s