Hot Posts

6/recent/ticker-posts

Lightning Web Components (LWC) Best Practice

Introduction to apex programming

 

1. LWC component Bundle naming convention

Check this post to learn about what is camelCase, PascalCase and kebab-case.

  1. Html file: Use camel case to name your component and use kebab-case to reference a component in the markup
  2. JavaScript File: Java Script Class name should be in PascalCase
  3. Bundle Component: use camelCase. 

2. Calling Apex From LWC

There are two ways to call Apex class in the Lightning web component.

  1. Imperatively
  2. Wire
    1. Wire a property
    2. Wire a function  

Wire Vs Imperatively:

As per Lightning component best practices, use @wire over imperative method invocation. @wire fits nicely in the overall Lightning Web Component reactive architecture. Salesforce is building some performance enhancement features that are only available with @wire. But there are a few use cases, that require you to use imperative Apex.

Wire Property Vs. Wire Function:

Prefer wiring to a property. This best practice applies to @wire generally (not just to wiring Apex methods).

3. Lightning Data Service (LDS)

As per LWC, the best practice is to use Lightning Data Service functions to create, Record, and delete a record over invoking Apex methods. Yes, there are some use cases where you need multiple records, then we can use Apex methods.

Lightning Data Service is built on top of User Interface API. UI API is a public Salesforce API that Salesforce uses to build Lightning Experience. As its name suggests, UI API is designed to make it easy to build Salesforce UI. UI API gives you data and metadata in a single response

Give preference to user interface form type in below order.

  1. lightning-record-form: It is the fastest/most productive way to build a form.
  2. lightning-record-view-form: If you need more control over the layout, want to handle events on individual input fields, or need to execute pre-submission
  3. @wire(getRecord): If you need even more control over the UI or if you need to access data without a UI.

4. Event in LWC

There are typically three approaches for communication between the components using events.

  1. Communication using Method in LWC ( Parent to Child )
  2. Custom Event Communication in Lightning Web Component (Child to Parent )
  3. Publish Subscriber model in Lightning Web Component Or Lightning Message Service ( Two components which don’t have a direct relation )

Here is some recommendation for the DOM Event.

  1. No uppercase letters
  2. No Spaces
  3. use underscores to separate words
  4. Don’t prefix your event name with the string “on”.


5. Streaming API, Platform Event, Change Data Capture

The lightning/empApi module provides access to methods for subscribing to a streaming channel and listening to event messages. All streaming channels are supported, including channels for platform events, PushTopic events, generic events, and Change Data Capture events. This component requires API version 44.0 or later. The lightning/empApi module uses a shared CometD connection. Example Code.

6. How to debug LWC

Use Chrome’s pretty JS setting to see unminified JavaScript and Debug Proxy Values for Data. Here is example.

  • Enable Debug mode
    • It gives unminified Javascript
    • Console warnings
    • Pretty data structure
  • Caution – it reduces the performance of Salesforce; make sure it’s disabled in production.


7. Use Storable Action/ Cache Data

Use Storable Action; It will reduce calls to the Server. Syntax

@AuraEnabled(cacheable=true)

Caution: A storable action might result in no call to the server. Never mark as storable an action that updates or deletes data.

For storable actions in the cache, the framework returns the cached response immediately and also refreshes the data if it’s stale. Therefore, Storable actions might have their callbacks invoked more than once: first with cached data, then with updated data from the server.

8. Build reusable Lightning Web Components

Modularity is key to creating a scalable application by reusing components. We rarely write code that is abstract enough to be reusable everywhere. You should write components not to be reused out of the box but to be abstract and composable sufficient to serve that purpose in various implementations.

Component Composition

Components are the building blocks of a page. We should consider the following point before dividing the components in LWC.

  • Level 1: Based on its functionality
  • Level 2: Based on its reusability: Passing attributes becomes a pain when you need to pass lots of inputs to the component. Consider passing objects as parameters instead of having multiple attributes.
LWC Best Practices to make reusable components.

Learn more about Design Patterns and Best Practices to build reusable Lightning Web Components.

9. Styling Reusable Components

LWC encapsulates and isolates a component’s stylesheet. Does it mean it is hard to style a child component from a parent?

  • Spend some time at design time to think about possible variants of your component.
  • Use CSS Custom Properties for custom styles
  • Use Styling Hooks to override the styling of Base Components.
  • Favor Styling Hooks and CSS Custom Properties over component attributes.

Check out how to Share CSS styles among Lightning Web Components.

10. LWC Best Practices

Here are some other LWC best practices that we can follow while building LWC components.

  • Use UI API Wire Adapters and Lightning Base Components
    • Allows the admin to configure your components without modifying code by configuring lists views record layouts.
    • If required, don’t hesitate to restrict your component’s visibility to certain objects.
  • Think whether the component needs to be tied to an object or can it be object-agnostic
    • Don’t use static references
    • If needed, create object-agnostic classes
  • Extract utility methods outside your LWC components
    • LWC components should only deal with UI-related logic.
  • Favor public properties over public methods.
    • Properties can be set directly in the template, while a method requires the consumer to render the component first, retrieve it back from the DOM, and invoke the method

11. Tips & Trick for Lightning Web Components

Here are 20 Tips and Trick for Lightning web components with examples.

1. Be careful while naming variables

Be careful while naming variables: Do not start a property name with ‘on’, ‘aria’, ‘data’. Also do not use reserved keywords like ‘slot’, ‘part’, ‘is’.

2. Avoid prefixing custom event names with ‘on’

Avoid prefixing custom event names with ‘on’: The event handler names start with on. So, adding an additional ‘on’ at the start of the event name would create confusion. So,

Instead of the below:

    sendData(){
 
        const customEvt = new CustomEvent('onselect');
        this.dispatchEvent(customEvt);
    }

Use this:

    sendData(){
 
        const customEvt = new CustomEvent('select');
        this.dispatchEvent(customEvt);
    }

3. LWC follows kebab-case naming convention

LWC follows kebab-case naming convention: Be extremely careful while naming Lightning Web Components. Similar to kebab-case format, each capital letter is represented as hyphen(-) followed by the lowercase version.

For example, if the component name is myCompLWC, it is referred as:

 <c-my-comp-l-w-c></c-my-comp-l-w-c>

Also as a best practice, start the component name with lowercase. If we start with an uppercase it will eventually be replaced with lowercase. For example, if we keep the name as MYComp it will be saved as mYComp.

And one more point, we need to mention the closing tags explicitly for lightning tags as well as while referencing components.

4. Use spread operator for adding additional attributes in data returned from apex

Use spread operator for adding additional attributes in data returned from apex: Many times we have some requirement where we need to add additional attributes to the data returned from apex. Suppose, we are getting the list of account records in LWC from apex. Now, we need to add an extra boolean attribute based on the type of account record to each of the account records.

For this kind of scenario, make use of map() function and spread operator like this:

 @wire(getLatestFiveAccounts)
    accountRecords({data, error}){
 
        if(data){
 
            this.accountList = data.map(record=>({
 
                ...record,
                'flag': record.Type==='Prospect' ? true : false
            })
 
            )
        }
 
    }

Learn more about how to call apex class from LWC.

5. Be mindful of case sensitivity

Be mindful of case sensitivity: LWC deals with case sensitivity. So, while writing codes we need to be extra careful.

For example, ‘message’ and ‘MESSAGE’ are two different properties. 

Similarly, while accessing Salesforce object fields, we need to mention the exact api name. For example, {account.Name} will return data but {account.name} won’t.

6. Always use dynamic field import in LWC

Always use dynamic field import in LWC: If you are importing any custom field in LWC, always use dynamic import. Because static imports are not referenced during field deletion, but dynamic imports are referenced. For example,

Instead of the below:

import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
 
const fields = ['CustomField1__c'];
 
export default class TestFieldAccess extends LightningElement {
 
    @api recordId;
 
    @wire(getRecord, { recordId: '$recordId', fields})
    accountRecord;
 
    get customFieldValue(){
 
        return getFieldValue(this.accountRecord.data, CUSTOM_FIELD1);
    }
 
}

Use this:

import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import CUSTOM_FIELD1 from '@salesforce/schema/Account.CustomField1__c';
 
const fields = [CUSTOM_FIELD1];
 
export default class TestFieldAccess extends LightningElement {
 
    @api recordId;
 
    @wire(getRecord, { recordId: '$recordId', fields})
    accountRecord;
 
    get customFieldValue(){
 
        return getFieldValue(this.accountRecord.data, CUSTOM_FIELD1);
    }
 
}

7. Use Getters instead of Expressions

Use Getters instead of Expressions: To compute a value for a property, use a JavaScript getter. Getters are more powerful than expressions because these are JavaScript functions and also enable unit testing, which reduces bugs and increases fun.

8. Do not forget to assign key within for:each

Do not forget to assign key within for:each: While using iteration in LWC, do not forget to assign the key attribute within the scope of the iteration. Otherwise, you will get the below error:

Elements within iterators must have a unique, computed key value.

9. Use iterator to apply special rendering to first and last elements of list

Use iterator to apply special rendering to first and last elements of list: If you want to apply special style or rendering to the first and last element of list, use iterator instead of for:each. Iterator has two more attributes: first and last.

For example, in the below example we are applying Bold styling to the first and last row.

 <template iterator:it={accountList}>
        <div key={it.value.Id}>
            <template if:true={it.first}> <!--first element-->
                <b>
                    Name: {it.value.Name}
                    type: {it.value.Type}
                </b>
            </template>
            <template if:false={it.first}> <!--if not first element-->
                <template if:true={it.last}> <!--last element-->
                    <b>
                        Name: {it.value.Name}
                        type: {it.value.Type}
                    </b>
                </template>
                <template if:false={it.last}> <!--if not last element-->
                        Name: {it.value.Name}
                        type: {it.value.Type}
                </template>
            </template>
        </div>
    </template>

10. Make use of multiple templates using render()

Make use of multiple templates using render(): If you want to render different UI based on certain conditions, render() function would be helpful in that case. You can import different html files in order to show different UI and from the render function choose which one to show and render the same using template if:true.

11. Pass only primitive data in custom event

Pass only primitive data in custom event: As one of the best practices, pass only primitive data types in custom events. JavaScript passes all data types by reference except for primitive data types. So, any listener can mutate the actual object which is not a good practice. 

If at all we need to pass an object, we need to copy the data to a new object before sending so that it does not get changed by any of the listeners.


12. Specify fields instead of layout in lightning-record-form

Specify fields instead of layout in lightning-record-form: To improve performance, specify specific fields instead of layout in lightning-record-form. If we specify layout, the component must handle receiving every field that is assigned to the layout for the context user.

13. Use LDS whenever possible

Use LDS whenever possible: Instead of making apex calls to get the data, try to use LDS. It requires no apex code and can perform Create, Read, Update, Delete operations on a single record. It also takes care of the sharing rules and field-level security.

14. use getRecord over getRecordUi

use getRecord over getRecordUi: getRecord returns record data while getRecordUi returns record data in addition to layout information and object metadata.

15. data and error property names are fixed in wire service

data and error property names are fixed in wire service: In case of wire service, the results of the apex method are provisioned to a function via an object with two property ‘data’ and ‘error’. 

We cannot put other name for these two variables since it is as per the standard. So, we need to refer these two property as ‘data’ and ‘error’ only. Any attempt to use some other name will fail silently and would impact the output.

For example, the below will fail silently and won’t show the data in the UI.

   @wire(getLatestFiveAccounts)
    accountRecords({data1, error1}){
 
        if(data1){
 
            this.accountList = data1.map(record=>({
 
                ...record,
                'flag': record.Type==='Prospect' ? true : false
            })
 
            )
        }
 
    }

16. Use dataset property for getting the data from selected row/record from UI

Use dataset property for getting the data from selected row/record from UI: Sometimes we get requirements where we need to get the data from a particular row which is selected from the UI. For this kind of scenario, use dataset property along with the record.

For example, we are showing a list of account records in the UI and each record has a button. If we want to get the particular record’s name on click of the button we can use dataset property like below:

    <template for:each={accountList} for:item="account">
        <div key={account.Id}>
            Name: {account.Name}
            flag: {account.flag}
            type: {account.Type}
        <lightning-button label="Click" data-name={account.Name} onclick={handleClick}></lightning-button>
    </div>
    </template>

.

handleClick(event){
 
        console.log(event.target.dataset.name);
    }

17. Refresh the cache using refreshApex() and getRecordNotifyChange()

Refresh the cache using refreshApex() and getRecordNotifyChange(): To get the updated data on the UI after some operation, make use of the refreshApex() and getRecordNotifyChange() methods for wire and imperative calls respectively.

18. Use targetConfig for configuring input to LWC

Use targetConfig for configuring input to LWC: To set the input in LWC from lightning app builder/flow/community make use of the targetConfig property.

19. Include error handling in the code logic

Include error handling in the code logic: We should consider the error scenario as well and notify the user with a user friendly error message. For this, we can make use of toast message to show high level detail to user on what went wrong.

20. Avoid using hardcoding, use custom labels

Avoid using hardcoding, use custom labels: Similar to apex, avoid using hardcoding in LWC. Keep the text inside a custom label and configure as and when required without making any change to existing code.

Learn more about custom labels in lwc and Share Custom labels between Lightning Web Components.


12. Lazy loading in LWC

Lazy loading helps you to load the data only when it is required. Infinite scrolling (enable-infinite-loading) enables you to load a subset of data and then load more data when users scroll to the end of the table.


Lightning Web Components (LWC) Best Practice Session

Hear from the man who helps make it happen: Salesforce Principal Developer Evangelist René Winkelmeyer. Building rich, efficient, and resilient Lightning Web Components is no black magic. This webinar will cover best practices around:

Agenda:-

  1. Using public and private properties for effective component composition.
    1. When should we use @api or @track variable
  2. Event communication for child-to-parent as well as for sibling components (pubsub).
    1. Parent to Child Communication
    2. Child to Parent Communication
    3. pubsub
  3. When, and when not, to use Apex with Lightning Web Components
  4. Aura interoperability

Post a Comment

0 Comments