streaming/selector-lifecycle
Choose StreamingStore selector call/subscription timing and withStore bindings. Direct Kefir calls require init() and are invalid after disposal; Store construction and teardown belong to streaming/st
Files1
View on GitHubSKILL.mdskills/streaming/selector-lifecycle/SKILL.md
GitHubStreaming selector lifecycle
Use this skill to decide when a Streaming selector can be invoked, observed, or bound to an alternate StreamingStore.
Use it only for apps that chose the Streaming Store family. Do not combine these Streaming lifecycle/setup rules with alternate Store or selector lifecycle/setup patterns in the same app.
For construction, initialization, and whole-Store shutdown, read ../store/SKILL.md — Lifecycle rules and Process bootstrap. This leaf owns when consumers may call and observe selectors, not how the Store runtime is initialized or disposed.
Lifecycle map
| Phase/context | Correct action | Why |
|---|---|---|
| Module setup | Define selectors with streamStore.createSelector(...) | Creation stores the selector callback; it does not read stream state yet. |
| Before init() | Avoid direct selector calls | They need the initialized Store-owned Kefir state stream. |
| After init() | Call selectFoo(...args) for a Kefir observable | The Store state stream is available. |
| Tests/composition | Use selectFoo.select(state, ...args) | Pure synchronous selector path, no Store lifecycle needed. |
| Sagas | Use yield* selectFoo.effect(...args) | Keeps named selector ownership and typed-redux-saga style. |
| Explicit StreamingStore binding | Use selectFoo.withStore(streamStore)(...args) | Binds to another initialized StreamingStore. |
| Consumer teardown | Unsubscribe each consumer when its owner ends, before whole-Store shutdown | Consumer ownership is separate from the Store lifetime in ../store/SKILL.md — Lifecycle rules. |
Operational guardrails
- Direct selector calls intentionally throw before
init()and afterdispose(); do not hide that error with fallback empty streams. - A direct selector call returns a Kefir observable. Manage observation/teardown using the consuming app's Kefir subscription pattern.
- Output reuse does not extend the valid invocation window; see
../selectors/SKILL.md— Selector caching for the identity/cache contract. .withStore(...)accepts another initialized StreamingStore; use it for tests/integration adapters that own their own initialized Store state.- For non-subscription read forms, use
../selectors/SKILL.md— Call forms. Selector-driven saga subscriptions instead follow../../core/selector-channels/SKILL.md— Do and Implementation cues, including the plain-argument tuple and saga-context state contract.
Consumer subscription ownership
The process bootstrap in ../store/SKILL.md — Process bootstrap initializes
the Store before starting consumers. Each Kefir consumer retains its own
subscription and stops it before that Store owner shuts down. For example, an
app-owned startConsumers function can return the observer's cleanup:
function startConsumers() {
const subscription = selectTodoCount().observe(handleTodoCount);
return () => subscription.unsubscribe();
}
Use the same ownership pattern for direct public diagnostic-stream observations;
the logger's own lifecycle remains in ../../core/redux-action-logging/SKILL.md —
Logger factory lifecycle and Store-owned logging streams. Do not import internal emitters to subscribe.
Verification cues
- Examples initialize the
StreamingStorebefore direct selector invocation. - Pre-init or post-dispose behavior is either avoided or explicitly tested as the documented selector state error.
- Streaming guidance instructs agents to initialize the
StreamingStorebefore direct observable observation.