> For the complete documentation index, see [llms.txt](https://triton.pharos.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://triton.pharos.ai/beyond-apex/profiling/component-lifecycle.md).

# Component Lifecycle

Knowing *when* a component shows up and *how often* it re-renders is half the battle for UI performance. Triton ships two helpers for this — `trackComponentLifecycle` and `trackComponentRender` — and both work transparently across multiple instances of the same component on a single page.

## `trackComponentLifecycle(event)`

Call this in `connectedCallback` and `disconnectedCallback` to log mount and unmount events. The accepted strings are `'connected'` and `'disconnected'`.

```javascript
connectedCallback() {
    this.triton = new Triton().bindToComponent('AccountDashboard');
    this.triton.trackComponentLifecycle('connected');
}

disconnectedCallback() {
    this.triton.trackComponentLifecycle('disconnected');
}
```

Each call produces a `TYPE.COMPONENT_LIFECYCLE` log entry with:

* `summary` = `Component lifecycle: <componentName> - <event>`
* `action` = `lifecycle-connected` or `lifecycle-disconnected`

These records are great for spotting components that mount unexpectedly, churn through DOM updates, or never get torn down properly (a common Aura → LWC migration headache).

## `trackComponentRender()`

Call from `renderedCallback`. Each invocation increments a per-instance counter and emits a `TYPE.COMPONENT_RENDER` log entry.

```javascript
renderedCallback() {
    this.triton.trackComponentRender();
}
```

The log entry includes:

* `summary` = `Component render: <componentName>`
* `details` = `Render count: <n>` — useful for spotting components that re-render an order of magnitude more than expected.

{% hint style="warning" %}
`renderedCallback` fires on every reactive update. If you instrument a noisy component, expect a lot of records. Combine with a sampling strategy (e.g. only log every Nth render) if you need to keep volume in check.
{% endhint %}

## Per-Instance Isolation

When you call `bindToComponent('ProductSearch')`, Triton mints a UUID v4 behind the scenes and stores tracking data under a key like:

```
ProductSearch-550e8400-e29b-41d4-a716-446655440000
```

That means three instances of `<c-product-search>` on the same page produce three independent streams of render counts, lifecycle events, and custom marks — even though the component name is identical.

```javascript
// Instance 1: ProductSearch-550e8400-e29b-41d4-a716-446655440000
// Instance 2: ProductSearch-7b2c5f8d-3e4a-4d5b-8c9f-1a2b3c4d5e6f
// Instance 3: ProductSearch-9a8b7c6d-5e4f-3d2c-1b0a-fedcba987654

export default class ProductSearch extends LightningElement {
    triton;

    connectedCallback() {
        this.triton = new Triton().bindToComponent('ProductSearch');
        this.triton.trackComponentLifecycle('connected');
    }

    renderedCallback() {
        this.triton.trackComponentRender();
    }
}
```

You don't need to manage instance IDs yourself — the proxy returned by `bindToComponent` carries them implicitly. The component name you pass is the *grouping* key (so dashboards can roll up across instances), and the UUID is the *uniqueness* key (so individual instances don't clobber each other's data).

## Initialization Timing

Where to bind depends on whether your component uses `@wire`. Same rule as in [LWC logging](/beyond-apex/lwc.md#creating-an-lwc-logger):

* **Without `@wire`** — bind and call `trackComponentLifecycle('connected')` inside `connectedCallback`.
* **With `@wire`** — bind in the constructor so the logger exists before wire callbacks fire, but still call `trackComponentLifecycle('connected')` from `connectedCallback` for the mount semantics to be accurate.

```javascript
export default class AccountDashboard extends LightningElement {
    triton;

    constructor() {
        super();
        this.triton = new Triton().bindToComponent('AccountDashboard');
    }

    @wire(getAccountData, { accountId: '$recordId' })
    wiredData({ error, data }) {
        // logger is already bound here
    }

    connectedCallback() {
        this.triton.trackComponentLifecycle('connected');
    }

    renderedCallback() {
        this.triton.trackComponentRender();
    }

    disconnectedCallback() {
        this.triton.trackComponentLifecycle('disconnected');
    }
}
```

## Locker Service-Safe Timing

Lifecycle and render entries timestamp themselves with `Date.now()` directly, and the broader profiling stack uses a `getCurrentTime()` helper that prefers `performance.now()` and silently falls back to `Date.now()` when the Performance API is restricted under Locker Service.

You don't need to special-case Locker Service in your code — every profiling helper in this section is built to degrade gracefully.

## What Happens Without `bindToComponent`

Both lifecycle and render helpers check the binding first. If you call them on an unbound logger:

```javascript
// MISSING bindToComponent
this.triton = new Triton();
this.triton.trackComponentLifecycle('connected'); // no-op, console warning
```

…they emit a console warning (`Triton performance tracking requires bindToComponent() to be called first`) and return. Nothing crashes; you just won't see the records.

## Best Practices

* Pair every `trackComponentLifecycle('connected')` with a `trackComponentLifecycle('disconnected')`. Missing disconnects are the smoking gun for memory-leak-shaped problems.
* Add `trackComponentRender()` early in the rollout to baseline normal render counts — once you have that baseline, regressions stand out instantly.
* Use the component name to your advantage: keep it stable and human-readable (the class name is usually the right call), so logs are easy to group and search.
