lwc icon indicating copy to clipboard operation
lwc copied to clipboard

event listener does not update when the variable used in template with onevent is updated

Open gaurav-rk9 opened this issue 1 year ago • 3 comments

Description

when a field is used in template as event handler and later assigned a new value , old value is still used to handle the event.

Steps to Reproduce

https://stackblitz.com/edit/salesforce-lwc-davchu?file=src%2Fmodules%2Fx%2Fcounter%2Fcounter.js

<template>
    <button onclick={mainHandler}>mainHandler</button>
    &nbsp;
    <button onclick={handlerChanger}>handlerChanger</button>
</template>
import { LightningElement } from 'lwc';

export default class extends LightningElement {
    mainHandler = () => {window.alert('initial');};
    handlerChanger = () => {
        this.mainHandler = () => {window.alert('changed');};
        window.alert(this.mainHandler);
    }
}

Expected Results

New value of mainHandler must be called

Actual Results

Old value of mainHandler is called

Browsers Affected

tested in Chrome : Version 131.0.6778.87 (Official Build) (arm64), probably in all

Version

  • LWC: 8.11.0

Possible Solution

Additional context/Screenshots Add any other context about the problem here. If applicable, add screenshots to help explain.

gaurav-rk9 avatar Dec 06 '24 14:12 gaurav-rk9

This is a limitation of the current LWC template compiler. One workaround is that you can use an object with a property rather than a plain function:

<template>
    <button onclick={handler.onClick}></button>
</template>
export default class extends LightningElement {
  handler = {
    onClick: () => { console.log('foo') }
  }
  connectedCallback() {
    this.handler.onClick = () => { console.log('bar') } // this will be used
  }
}

Demo

nolanlawson avatar Dec 06 '24 17:12 nolanlawson

BTW the current behavior has some performance benefits, namely that we can bind/cache the event listener once rather than re-evaluating it. So fixing this may incur a performance cost.

nolanlawson avatar Dec 06 '24 17:12 nolanlawson

One workaround is that you can use an object with a property rather than a plain function:

<template>
    <button onclick={handler.onClick}></button>
</template>
export default class extends LightningElement {
  handler = {
    onClick: () => { console.log('foo') }
  }
  connectedCallback() {
    this.handler.onClick = () => { console.log('bar') } // this will be used
  }
}

Demo

The example provided here works because the connectedCallback executes before the first render. The original issue mentioned is reproducible even with such objects, if the update is after the first render.

gaurav-rk9 avatar Feb 20 '25 06:02 gaurav-rk9