contextRegistry

Register context instances and resolve them by their runtime class.

contextRegistryLifecycle and Scope

Register context instances and resolve them by their runtime class.

Overview

ContextRegistry is a utility class for explicit context sharing across component trees.

It stores context instances by constructor and lets you resolve them by type. Lookup uses instanceof, so querying by a base class can return a subclass instance.

Try it live

Resolve the instance you registeredLive API
RESOLVED BY CLASSNothing registered yet.

The registry stores an instance. Editing the draft alone does not replace it.

import { ContextRegistry, html, ref } from 'regor'

class Workspace {
  constructor(readonly name: string) {}
}

export function createRegistry(interactive = false) {
  const registry = new ContextRegistry()
  const name = ref('Regor Studio'),
    resolved = ref('Nothing registered yet.')
  const register = () => {
    registry.register(new Workspace(name()))
    resolved(registry.require(Workspace).name)
  }
  const clear = () => {
    registry.unregisterByClass(Workspace)
    resolved(registry.find(Workspace)?.name ?? 'Nothing registered yet.')
  }
  return { interactive, name, resolved, register, clear }
}

export const registryTemplate = html` <div class="guide-demo guide-demo--split">
  <div class="guide-controls">
    <label for="registry-name">Workspace instance</label
    ><input
      id="registry-name"
      type="text"
      maxlength="40"
      r-model="name"
      :value="name"
      :disabled="!interactive"
    />
    <div class="guide-session-bar">
      <button type="button" @click="register" :disabled="!interactive">
        Register / replace</button
      ><button type="button" @click="clear" :disabled="!interactive">
        Unregister
      </button>
    </div>
  </div>
  <div class="guide-readout">
    <span class="guide-kicker">RESOLVED BY CLASS</span
    ><output class="directive-output" role="status">{{ resolved }}</output>
    <p>
      The registry stores an instance. Editing the draft alone does not replace
      it.
    </p>
  </div>
</div>`
import { createApp, RegorConfig, useScope } from 'regor'
import { apiExamples } from './examples'

const element = document.getElementById('api-demo')
const name = element?.dataset.example
if (element && name && Object.hasOwn(apiExamples, name)) {
  const example = apiExamples[name as keyof typeof apiExamples]
  const scope = useScope<object>(() => example.create(true))
  const config =
    'config' in scope.context && scope.context.config instanceof RegorConfig
      ? scope.context.config
      : undefined
  createApp(scope, { element, template: example.template }, config)
}

Register a workspace, replace it, or unregister it. Lookup uses the class rather than the draft input.

Usage

import { ContextRegistry } from 'regor'

class AppServices {
  readonly apiBase = '/v1'
}

const registry = new ContextRegistry()
registry.register(new AppServices())

const services = registry.require(AppServices)

API

register(context)

Registers an instance under its runtime constructor.

  • If another instance of the same constructor is already registered, it is replaced.

unregisterByClass(ContextClass)

Removes the entry for a constructor.

  • No-op when that constructor is not registered.

unregister(context)

Removes an instance only if it is currently the registered value for its constructor.

  • This prevents deleting a newer replacement instance registered later.

find(ContextClass)

Returns the first registered instance matching instanceof ContextClass, or undefined.

require(ContextClass)

Returns the same result as find, but throws when not found.

  • Error message format: ClassName is not registered in ContextRegistry.

Example: parent provides registry, child resolves from head.requireContext

import { ComponentHead, ContextRegistry, defineComponent } from 'regor'

class AppServices {
  readonly apiBase = '/v1'
}

class Parent {
  readonly registry = new ContextRegistry()

  constructor() {
    this.registry.register(new AppServices())
  }
}

class Child {
  readonly apiBase: string

  constructor(head: ComponentHead<object>) {
    // Parent can provide registry from any component context shape.
    const parent = head.requireContext(Parent)
    const services = parent.registry.require(AppServices)
    this.apiBase = services.apiBase
  }
}

const parent = defineComponent<Parent>('<div><slot></slot></div>', {
  context: () => new Parent(),
})

const child = defineComponent<Child>('<p r-text="apiBase"></p>', {
  context: (head) => {
    return new Child(head)
  },
})

Example: component ctx <=> component ctx communication through parent registry

import {
  ComponentHead,
  ContextRegistry,
  createApp,
  defineComponent,
  html,
  ref,
} from 'regor'

class Parent {
  components = { producer, consumer }
  registry = new ContextRegistry()
}

class Consumer {
  message = ref('idle')
  private readonly parent: Parent

  constructor(head: ComponentHead<object>) {
    this.parent = head.requireContext(Parent)
    this.parent.registry.register(this)
  }

  receive = (next: string): void => {
    this.message(next)
  }
}

class Producer {
  private readonly parent: Parent

  constructor(head: ComponentHead<object>) {
    this.parent = head.requireContext(Parent)
    this.parent.registry.register(this)
  }

  send = (): void => {
    // direct component-ctx -> component-ctx call through registry
    const consumer = this.parent.registry.require(Consumer)
    consumer.receive('message-from-producer')
  }
}

const producer = defineComponent<Producer>(
  html`<button class="send" @click="send">send</button>`,
  {
    context: (head) => new Producer(head),
  },
)

const consumer = defineComponent<Consumer>(
  html`<p class="value">{{ message }}</p>`,
  {
    context: (head) => new Consumer(head),
  },
)

const parent = defineComponent<Parent>(
  html`<section>
    <Producer></Producer>
    <Consumer></Consumer>
  </section>`,
  {
    context: () => new Parent(),
  },
)

createApp(
  { components: { parent } },
  {
    element: document.querySelector('#app')!,
    template: '<Parent></Parent>',
  },
)
Store instances with explicit identity

Registering another instance of the same constructor replaces the earlier entry. find can check for absence without throwing.

See Also

Related APIs and guides Continue with the references connected to contextRegistry.

Back to the API list