In Salesforce Aura Lightning components, a lightning:inputField with an onchange event handler on a picklist field may fire an extra onchange event during the initial page load or component refresh.
This occurs because when field values are loaded from the Salesforce API, the component triggers a change event as part of its initialization — before the user interacts with the field.
For example, when a picklist field is set to —None— on initial load, the onchange handler fires immediately. This causes unintended behavior such as a Save button appearing before the user has changed anything.
Sample scenario: A custom Aura Lightning component on an Opportunity record page uses a lightning:inputField picklist with an onchange handler that shows a Save button. The Save button appears immediately on page load even though the user has not changed the field value.
Sample setup
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="displaySave" type="boolean" default="false" description="Displays Save button if changed"/>
<lightning:recordEditForm aura:id="recordEditForm"
recordId="{!v.recordId}"
objectApiName="Opportunity"
onsuccess="{!c.onSuccess}"
>
<lightning:messages />
<lightning:inputField fieldName="Custom_Picklist__c" aura:id="picklistField" onchange="{!c.onPicklistChange}"/>
<aura:if isTrue="{!v.displaySave}">
<lightning:button variant="brand" label="Save" type="submit"/>
</aura:if>
</lightning:recordEditForm>
</aura:component>
JS Controller:
({
onPicklistChange : function(component, event, helper)
{
component.set("v.displaySave", "true");
},
onSuccess: function(component, event, helper)
{
component.set("v.displaySave", false);
}
})
This behavior is expected in Salesforce Aura Lightning components.When field data loads from the API, the component initializes field values, which triggers the onchange event.
The best practice is to design your onchange handler so it correctly distinguishes between initialization-triggered changes and user-triggered changes.
Add an onload event handler to your lightning:recordEditForm to capture the initial record state. In the onPicklistChange function, compare the new value against the stored initial state. If the values match (meaning no user change has occurred), do not show the Save button.
How it works:
onload handler fires and stores a snapshot of the current record fields (using JSON.parse(JSON.stringify(...))) into a component attribute called currentRecord.onchange fires, the handler retrieves the field name and the new value from the event source.null and the new value is an empty string, this is the initialization trigger — no user action has occurred, so displaySave is not set to true.displaySave is set to true and the Save button appears.onSuccess), displaySave is reset to false.Key changes from the original setup:
onload="{!c.onload}" to the lightning:recordEditForm component.currentRecord attribute of type Object to track field values.onPicklistChange to compare the incoming value against the stored initial record state before deciding to show the Save button.onload function to the JS Controller to capture the initial record fields.<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="displaySave" type="boolean" default="false" description="Displays Save button if changed"/>
<aura:attribute name="currentRecord" type="Object"/><!--Added Attribute to track-->
<lightning:recordEditForm aura:id="recordEditForm"
recordId="{!v.recordId}"
objectApiName="Opportunity"
onsuccess="{!c.onSuccess}"
onload="{!c.onload}"
><!--Added onload onload="{!c.onload}"-->
<lightning:messages />
<lightning:inputField fieldName="Custom_Picklist__c" aura:id="picklistField" onchange="{!c.onPicklistChange}" />
<aura:if isTrue="{!v.displaySave}">
<lightning:button variant="brand" label="Save" type="submit"/>
</aura:if>
</lightning:recordEditForm>
</aura:component>
JS Controller
({
onPicklistChange : function(component, event, helper)
{
let fieldName = event.getSource().get("v.fieldName") ;
let newValue = event.getSource().get("v.value") ;
let currentRecord = component.get("v.currentRecord") ;
if(currentRecord[fieldName].value ==null && newValue == "" ) { //we know the value is blank from the UI
currentRecord[fieldName].value = newValue ;
component.set("v.currentRecord", currentRecord);
}
else{
component.set("v.displaySave", "true");
}
},
onSuccess: function(component, event, helper)
{
component.set("v.displaySave", false);
} ,
onload: function(component, event, helper)
{
if(event.getParam("recordUi").record){
component.set("v.currentRecord", JSON.parse(JSON.stringify(event.getParam("recordUi").record.fields)));
}
}
})
000381002

We use three kinds of cookies on our websites: required, functional, and advertising. You can choose whether functional and advertising cookies apply. Click on the different cookie categories to find out more about each category and to change the default settings.
Privacy Statement
Required cookies are necessary for basic website functionality. Some examples include: session cookies needed to transmit the website, authentication cookies, and security cookies.
Functional cookies enhance functions, performance, and services on the website. Some examples include: cookies used to analyze site traffic, cookies used for market research, and cookies used to display advertising that is not directed to a particular individual.
Advertising cookies track activity across websites in order to understand a viewer’s interests, and direct them specific marketing. Some examples include: cookies used for remarketing, or interest-based advertising.