Skip to content
GitHubXDiscord

TriggerScope

Stable · since 0.1.0

Provides a scope id to descendants. useCondition and useAction calls inside this subtree register with the scope, and only triggers declared with the same scope in createTrigger(...) will see them. The runtime, inspector, scheduler and middleware all stay shared — TriggerScope is a lighter, in-runtime partition compared to <TriggerRuntimeProvider>.

Use it when one runtime is correct but you need two halves of the tree to register independent providers for the same condition or action.

import { TriggerScope } from '@triggery/react';
function TriggerScope(props: TriggerScopeProps): JSX.Element;

type TriggerScopeProps = {
  readonly id: string;
  readonly children: ReactNode;
};
PropTypeRequiredDescription
idstringyesScope id. Must match the scope field on a trigger to bind.
childrenReactNodeyesSubtree where registrations are tagged with id.
import { createTrigger } from '@triggery/core';
import { TriggerScope, useAction, useCondition, useEvent } from '@triggery/react';

const messageTrigger = createTrigger<{
  events: { 'new-message': { author: string; text: string } };
  conditions: { activeChannelId: string };
  actions: { showToast: { title: string } };
}>({
  id: 'chat:new-message',
  scope: 'chat',
  events: ['new-message'],
  required: ['activeChannelId'],
  handler({ event, conditions, actions }) {
    if (event.payload.author === conditions.activeChannelId) return;
    actions.showToast?.({ title: event.payload.author });
  },
});

function ChatA() {
  useCondition(messageTrigger, 'activeChannelId', () => 'a', []);
  useAction(messageTrigger, 'showToast', t => console.log('toast A', t.title));
  return null;
}
function ChatB() {
  useCondition(messageTrigger, 'activeChannelId', () => 'b', []);
  useAction(messageTrigger, 'showToast', t => console.log('toast B', t.title));
  return null;
}

function Page() {
  return (
    <>
      <TriggerScope id="chat">
        <ChatA />
      </TriggerScope>
      <TriggerScope id="chat">
        {/* Second scope — registrations from ChatB would override ChatA's
            but they're in a different boundary. Use distinct ids if you want
            truly independent buckets. */}
        <ChatB />
      </TriggerScope>
    </>
  );
}

Independent scopes for two trigger instances

Section titled “Independent scopes for two trigger instances”
import { createTrigger } from '@triggery/core';
import { TriggerScope, useCondition } from '@triggery/react';

const triggerLeft = createTrigger<{
  events: { ping: void };
  conditions: { value: number };
}>({ id: 'left', scope: 'left', events: ['ping'], required: ['value'], handler() {} });

const triggerRight = createTrigger<{
  events: { ping: void };
  conditions: { value: number };
}>({ id: 'right', scope: 'right', events: ['ping'], required: ['value'], handler() {} });

function Layout() {
  return (
    <>
      <TriggerScope id="left">
        <Provider t={triggerLeft} v={1} />
      </TriggerScope>
      <TriggerScope id="right">
        <Provider t={triggerRight} v={2} />
      </TriggerScope>
    </>
  );
}

function Provider({ t, v }: { t: ReturnType<typeof createTrigger>; v: number }) {
  useCondition(t, 'value', () => v, [v]);
  return null;
}

The inner id wins for everything inside it:

import { TriggerScope } from '@triggery/react';

<TriggerScope id="outer">
  {/* registrations here tagged "outer" */}
  <TriggerScope id="inner">
    {/* registrations here tagged "inner" */}
  </TriggerScope>
</TriggerScope>