@contextSelector
Use @contextSelector(...) on a field to bind it to the current context value or a selected slice of it.
When the selected value changes, the field is updated and — on a render-owning host such as RadiantElement or a render-owning RadiantController — a coalesced requestUpdate() is scheduled automatically. No empty method body. No manual this.update() call.
Options
| Option | Type | Description |
|---|---|---|
context | Context | The context token to resolve |
select | (context) => Selected | Optional projection. Narrows the resolved value before it is written to the field |
subscribe | boolean | Whether client-side subscriptions stay active after the first value. Defaults to true |
Example
import { RadiantElement, customElement } from '@ecopages/radiant';
import { contextSelector } from '@ecopages/radiant/context';
import { todoContext, type Todo } from './todo-context';
@customElement('todo-board')
export class TodoBoard extends RadiantElement {
@contextSelector({ context: todoContext, select: ({ todos }) => todos })
todos: Todo[] = [];
override render() {
return (
<ul>
{this.todos.map((todo) => (
<li>{todo.text}</li>
))}
</ul>
);
}
}render() reads this.todos directly. When the todo list changes in context, the field is updated and the host rerenders automatically.
RadiantController Example
import { RadiantController } from '@ecopages/radiant';
import { contextSelector } from '@ecopages/radiant/context';
import { todoContext, type Todo } from './todo-context';
export class TodoPanelController extends RadiantController {
@contextSelector({ context: todoContext, select: ({ todos }) => todos })
todos: Todo[] = [];
override render() {
return (
<ul>
{this.todos.map((todo) => (
<li>{todo.text}</li>
))}
</ul>
);
}
}Full context on a field
Omit select to bind the whole context value to the field. This is the right choice when the derived view value also depends on instance state — such as a @prop — that the select function cannot access.
@customElement('insight-card')
export class InsightCard extends RadiantElement {
@prop({ type: String }) kind: 'commits' | 'stage' | 'tempo' = 'stage';
@contextSelector({ context: boardContext })
context: BoardContext | undefined;
override render() {
let summary = 'Pending';
if (this.context) {
switch (this.kind) {
case 'commits': summary = `${this.context.commits} synced`; break;
case 'tempo': summary = this.context.tempo; break;
default: summary = this.context.stage;
}
}
return <p>{summary}</p>;
}
}The key constraint: select only receives the context value — it has no access to this. Whenever the derived value depends on instance state, bind the full context and compute in render(). Do not introduce an intermediate @state field or an @onContextUpdate callback just to copy the result.
For side effects: use @onContextUpdate
When the context change should trigger imperative work rather than a rerender, use @onContextUpdate on a method instead.
import { onContextUpdate } from '@ecopages/radiant/context';
@customElement('analytics-tracker')
export class AnalyticsTracker extends RadiantElement {
@onContextUpdate({ context: cartContext, select: ({ total }) => total })
onTotalChanged(total: number) {
this.setAttribute('data-cart-total', String(total));
}
}When To Use Which
| Decorator | Target | Use when |
|---|---|---|
@contextSelector(...) | field | The host should render from the current context value |
@onContextUpdate(...) | method | A context change should trigger imperative side effects |
@consumeContext(...) | field | The host needs the provider object to call setContext(...) |
Subscribe Behavior
On the client, the field keeps receiving updates according to subscribe. During SSR, the field can also be written when an ambient provider is available.
When To Use It
- Use
@contextSelector(...)on a render-owning host field whenrender()should read from the current context value. - Use @onContextUpdate when the response is imperative work.
- Use @consumeContext when the host needs to write to context via
setContext(...).