LearnFrenzy
Sign in Take a free test

Functions, this and Closures · Lesson 5 of 25

The this Keyword in LWC

5 min read Updated Nov 8, 2026 Saurabh Samir

Inside a component method, this is the component – so this.count and this.template just work. Then one day you pass a method to setTimeout or window.addEventListener and get "Cannot read properties of undefined". Understanding why takes five minutes and saves hours.

In this lesson

  • The rule: this is decided by how a function is called
  • Why template handlers work
  • Losing this in callbacks – and three fixes
  • Adding and removing window listeners correctly

The rule

For a normal function or method, this is whatever is left of the dot at call time. Arrow functions don't have their own this – they use the this of the code around them.

How this is decided

  1. obj.method()this = obj
  2. const f = obj.method; f()this = undefined (strict mode) – the dot is gone
  3. f.bind(obj)a new function with this fixed to obj
  4. () => this.xarrow: this from the surrounding code
class Counter {
    count = 0;
    increment() {
        this.count++;
        return this.count;
    }
}

const counter = new Counter();
console.log(counter.increment());          // called with a dot: this = counter

const loose = counter.increment;           // the method, detached from its object
try {
    loose();
} catch (e) {
    console.log(e.name + ': ' + e.message);
}

const bound = counter.increment.bind(counter);
console.log(bound());                      // bind fixes this
const viaArrow = () => counter.increment();
console.log(viaArrow());                   // the arrow calls it with a dot

Output

1
TypeError: Cannot read properties of undefined (reading 'count')
2
3

Why template handlers just work

When you write onclick={handleClick}, the LWC engine calls your method with this set to the component. That's why you never need bind for handlers declared in the template. Problems only start when you hand a method to someone else.

Losing this in timers and callbacks

// countdownTimer.js
import { LightningElement } from 'lwc';

export default class CountdownTimer extends LightningElement {
    seconds = 3;
    intervalId;

    connectedCallback() {
        // ✗ setInterval(this.tick, 100) would call tick() without a dot: this is undefined inside
        // ✓ an arrow function keeps the component's this
        // eslint-disable-next-line @lwc/lwc/no-async-operation
        this.intervalId = setInterval(() => this.tick(), 100);
    }

    tick() {
        this.seconds -= 1;
        if (this.seconds === 0) clearInterval(this.intervalId);
    }

    disconnectedCallback() {
        clearInterval(this.intervalId);       // always clean up
    }

    get label() {
        return this.seconds > 0 ? `Starting in ${this.seconds}…` : 'Go!';
    }
}
<!-- countdownTimer.html -->
<template>
    <p>{label}</p>
</template>

Result

Starting in 3…

› wait 150ms
Starting in 2…

› wait 200ms
Go!

The same applies to array.map(this.format) if format uses this, and to promise callbacks: write .then((result) => this.handleResult(result)).

About the eslint-disable comment

LWC's lint rule @lwc/lwc/no-async-operation warns on every setTimeout/setInterval, because timers are often used to "wait for rendering" – which is fragile. When a timer is genuinely what you need (a clock, a debounce), disable the rule for that line and make sure you clear it.

Window and document listeners

Listening to events outside your component – window resize, a keyboard shortcut – means calling addEventListener yourself, and removing it when the component goes away. removeEventListener only works with the same function reference, so this.handleKey.bind(this) written twice won't match. Store the bound function in a field:

// shortcutListener.js
import { LightningElement } from 'lwc';

export default class ShortcutListener extends LightningElement {
    lastKey = 'none';

    // a class field holding an arrow function: one stable reference, this always the component
    handleKeyDown = (event) => {
        this.lastKey = event.key;
    };

    connectedCallback() {
        window.addEventListener('keydown', this.handleKeyDown);
    }

    disconnectedCallback() {
        window.removeEventListener('keydown', this.handleKeyDown);
    }
}
<!-- shortcutListener.html -->
<template>
    <p>Last key: {lastKey}</p>
</template>

Forgetting removeEventListener is a real memory leak: the window keeps a reference to the removed component, and its handler keeps running every time the user presses a key.

Arrow-function class fields vs methods

method handle() {}arrow field handle = () => {}
this when passed as a callbacklostalways the component
Storedonce, on the prototypeone copy per component instance
Best fortemplate handlers, normal methodslisteners you add and remove yourself

Practice

Try it yourself

  1. This code throws "Cannot read properties of undefined (reading 'records')". Why, and what's the smallest fix?
    getContacts().then(function (data) { this.records = data; });

    Show answer

    A regular function callback gets its own this, which is undefined when the promise calls it. Use an arrow: .then((data) => { this.records = data; }).

  2. Why doesn't window.removeEventListener('resize', this.onResize.bind(this)) remove the listener?

    Show answer

    bind creates a new function every time, so it isn't the function that was added. Bind once and store it (or use an arrow-function field).

Quick check

What is this in a detached method call in strict mode?

undefined.

Do arrow functions have their own this?

No – they use the this of the surrounding code.

Do template handlers need bind?

No – LWC calls them with this set to the component.

Summary

  • this is set by how a function is called; arrows inherit it.
  • Template handlers are safe; callbacks you pass yourself need an arrow or bind.
  • Store listeners in a field so add and remove use the same reference.
  • Clear timers and remove listeners in disconnectedCallback.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links