RegisterReactiveGlobalContext
registerReactiveGlobalContext
Written for AI agents. See Log Methodology Note below for details.
Background
Design Log #65 introduced registerGlobalContext for registering app-wide contexts during initialization via makeJayInit(). However, this only supports static contexts - the value is stored as-is without reactivity.
For component-provided contexts, we have:
provideContext(marker, value)- static contextprovideReactiveContext(marker, mkContext)- reactive context with signals
The reactive version uses createReactiveContext() which:
- Creates a
Reactiveinstance for signal tracking - Sets up
CONTEXT_CREATION_CONTEXTsocreateSignal()works inside the factory - Returns a proxy that batches reactions for function calls
Problem
There's no equivalent for global contexts. When using registerGlobalContext in withClient, signals created inside the context factory won't work because there's no reactive system set up.
// ❌ Won't work - signals have no reactive system
export const init = makeJayInit().withClient(() => {
const [count, setCount] = createSignal(0); // Error: no reactive context
registerGlobalContext(COUNTER_CTX, { count, setCount });
});
Design
Add registerReactiveGlobalContext to @jay-framework/component:
export function registerReactiveGlobalContext<T extends object>(
marker: ContextMarker<T>,
mkContext: () => T,
): T;
Behavior
- Creates a
Reactiveinstance viamkReactive('global-ctx', marker.description) - Sets up
CONTEXT_CREATION_CONTEXTwith:- The reactive instance
mountedSignalinitialized totrue(always mounted)- Empty
provideContexts(not used for global contexts)
- Calls the factory function within this context
- Wraps result in a proxy that batches reactions
- Calls
registerGlobalContext(marker, wrappedContext) - Returns the wrapped context (useful for accessing in the same init)
Usage
// ✅ Works - signals have reactive system
import { registerReactiveGlobalContext } from '@jay-framework/component';
export const init = makeJayInit().withClient(() => {
registerReactiveGlobalContext(COUNTER_CTX, () => {
const [count, setCount] = createSignal(0);
return { count, setCount, increment: () => setCount((n) => n + 1) };
});
});
Implementation Location
@jay-framework/component/lib/context-api.ts - co-located with createReactiveContext
This makes sense because:
- Already has
createReactiveContextlogic - Has access to
CONTEXT_CREATION_CONTEXT componentpackage depends on bothreactiveandruntime
Code
// In component/lib/context-api.ts
export function registerReactiveGlobalContext<T extends object>(
marker: ContextMarker<T>,
mkContext: () => T,
): T {
const context = createReactiveContext(mkContext);
registerGlobalContext(marker, context);
return context;
}
This is simple because createReactiveContext already does all the heavy lifting.
Implementation Plan
Phase 1: Add the function
- Add
registerReactiveGlobalContexttocomponent/lib/context-api.ts - Export from
component/lib/index.ts - Add tests
Phase 2: Update documentation
- Update
packages/runtime/component/docs/withregisterReactiveGlobalContextusage - Update
docs/core/components.mdwith global reactive context section
Phase 3: Update wix-server-client example
Update the example in wix/packages/wix-server-client to use reactive global context if needed.
Examples
Counter Context
// lib/contexts/counter.ts
export interface CounterContext {
count: Getter<number>;
increment: () => void;
}
export const COUNTER_CTX = createJayContext<CounterContext>();
// lib/init.ts
export const init = makeJayInit().withClient(() => {
registerReactiveGlobalContext(COUNTER_CTX, () => {
const [count, setCount] = createSignal(0);
return {
count,
increment: () => setCount((n) => n + 1),
};
});
});
Wix Client Context (Reactive)
export const init = makeJayInit().withClient(async () => {
await registerReactiveGlobalContext(WIX_CLIENT_CONTEXT, () => {
const [isReady, setIsReady] = createSignal(false);
const [tokens, setTokens] = createSignal<Tokens | null>(null);
return {
client: wixClient,
isReady,
tokens,
async initialize() {
const newTokens = await wixClient.auth.generateVisitorTokens();
setTokens(newTokens);
setIsReady(true);
},
};
});
});
Trade-offs
Return Value
Option A: Return the context (proposed)
- ✅ Allows using the context immediately in init
- ✅ Consistent with
provideReactiveContextwhich also returns the context
Option B: Return void (like registerGlobalContext)
- ✅ Consistent with
registerGlobalContext - ❌ Need to use
useContextto access, which may not be set up yet
Going with Option A for flexibility.
Questions
Q1: Should this be async-capable?
Answer: No. The function is sync. Contexts that need async initialization should expose an init() method (or similar). This keeps the API simple and works well with hooks.
registerReactiveGlobalContext(CTX, () => {
const [ready, setReady] = createSignal(false);
return {
ready,
async init() {
await doAsyncWork();
setReady(true);
},
};
});
// In withClient:
const ctx = registerReactiveGlobalContext(CTX, () => ...);
await ctx.init();
Implementation Results
Phase 1: Add the function ✅
- Added
registerReactiveGlobalContexttocomponent/lib/context-api.ts - Already exported via
export * from './context-api'inindex.ts - Implementation reuses
createReactiveContextand callsregisterGlobalContext
Phase 2: Tests ✅
Added 5 tests in context-api.test.ts:
registers a reactive context globallyreturns the created context for immediate usesupports async init patternconsuming component should read a value from a global reactive contextcomponent should react to signal changes in global reactive context
All 56 tests pass.
Phase 3: Documentation ✅
- Created
packages/runtime/component/docs/register-reactive-global-context.md - Updated
docs/core/components.mdwith "Global Reactive Contexts" section - Updated
packages/runtime/component/readme.mdto reference new doc
Real-world Usage: wix-stores ✅
Updated wix/packages/wix-stores to use registerReactiveGlobalContext:
WixStoresContextnow has reactivecartIndicatorsignals (itemCount,hasItems)- Added
addToCart(productId, quantity?, variantId?)that updates indicator - Added
refreshCartIndicator()to fetch initial state - Init calls
refreshCartIndicator()on startup
Bug Fix: Context Markers in Generated Client Script ✅
Fixed issue where context markers were not included in the generated client script.
Problem: In load-page-parts.ts, context markers were hardcoded to []:
clientPart: `{comp: ${name}.comp, contextMarkers: [], key: '${key}'}`;
This caused components with .withContexts(...) to not receive their contexts.
Solution: Reference the component's .contexts property:
clientPart: `{comp: ${name}.comp, contextMarkers: ${name}.contexts || [], key: '${key}'}`;
Now global contexts registered via registerReactiveGlobalContext are properly resolved via useContext() in component constructors.
Files Modified
jay/packages/runtime/component/lib/context-api.tsjay/packages/runtime/component/test/context-api.test.tsjay/packages/runtime/component/docs/register-reactive-global-context.md(new)jay/packages/runtime/component/readme.mdjay/docs/core/components.mdjay/packages/jay-stack/stack-server-runtime/lib/load-page-parts.tsjay/packages/jay-stack/dev-server/test/dev-server.test.tswix/packages/wix-stores/lib/contexts/wix-stores-context.tswix/packages/wix-stores/lib/init.tswix/packages/wix-stores/lib/index.client.ts
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.