SignalTrackingController
A Lit ReactiveController that allows you to explicitly track specific signals and trigger updates on the host component when those signals mutate.
API Signature
typescript
export class SignalTrackingController implements ReactiveController {
constructor(
host: ReactiveElement,
track: () => void
)
}- Parameters:
host: The Lit host element (usuallythisinside your component class).track: A tracking callback function. Any signal read inside this function is registered as a dependency.
Why use the Controller instead of the Mixin?
While SignalWatcher is the easiest way to handle reactivity automatically, the SignalTrackingController is preferred in the following scenarios:
- Explicit Dependencies: If you want to explicitly declare what signals a component is watching, rather than tracking everything read in the
render()template. - Outside the Render Cycle: If you need to trigger updates based on signals read in custom controllers, side-effects, or lifecycle methods (e.g. tracking inside a custom animation loop).
- Third-Party Integrations: If you are writing custom Lit controllers or integrations that need to handle their own signal-based reactivity.
How it works (Under the Hood)
- The controller registers itself to the host Lit element using
host.addController(this). - When the host connects to the DOM (
hostConnected), the controller spawns aneffectScopeand registers aneffectaround the providedtrackfunction. - When any signal accessed inside
track()changes, the effect is triggered, which in turn callsthis.host.requestUpdate(). - When the host disconnects (
hostDisconnected), the effect scope is destroyed and all subscription handlers are disposed.
Detailed Example
typescript
import { signal } from 'alien-signals'
import { LitElement, html } from 'lit'
import { customElement } from 'lit/decorators.js'
import { SignalTrackingController } from 'alien-lit'
const userRole = signal('guest')
@customElement('admin-panel')
export class AdminPanel extends LitElement {
// Explicitly subscribe to changes in the 'userRole' signal
private roleTracker = new SignalTrackingController(this, () => {
userRole() // Accessing the signal registers it as a dependency
})
override render() {
return html`
<div>
<p>Current Role: ${userRole()}</p>
${userRole() === 'admin'
? html`<button>Manage Users</button>`
: html`<span>Access Denied</span>`}
</div>
`
}
}Live Interactive Preview
Below is a live instance of a component using SignalTrackingController (observing the shared count signal):