@directive-run/claude-plugin 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +6 -0
- package/LICENSE +26 -0
- package/README.md +113 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/skills/README.md +9 -0
- package/skills/building-ai-agents/SKILL.md +414 -0
- package/skills/building-ai-agents/ai-adapters.md +257 -0
- package/skills/building-ai-agents/ai-agents-streaming.md +307 -0
- package/skills/building-ai-agents/ai-communication.md +304 -0
- package/skills/building-ai-agents/api-skeleton.md +5 -0
- package/skills/building-ai-agents/examples.md +849 -0
- package/skills/building-ai-orchestrators/SKILL.md +401 -0
- package/skills/building-ai-orchestrators/ai-multi-agent.md +375 -0
- package/skills/building-ai-orchestrators/ai-orchestrator.md +466 -0
- package/skills/building-ai-orchestrators/ai-tasks.md +268 -0
- package/skills/building-ai-orchestrators/api-skeleton.md +5 -0
- package/skills/building-ai-orchestrators/examples.md +1609 -0
- package/skills/building-directive-systems/SKILL.md +499 -0
- package/skills/building-directive-systems/api-skeleton.md +5 -0
- package/skills/building-directive-systems/examples.md +4149 -0
- package/skills/building-directive-systems/multi-module.md +327 -0
- package/skills/building-directive-systems/plugins.md +361 -0
- package/skills/building-directive-systems/react-adapter.md +376 -0
- package/skills/building-directive-systems/system-api.md +463 -0
- package/skills/getting-started-with-directive/SKILL.md +198 -0
- package/skills/getting-started-with-directive/api-skeleton.md +5 -0
- package/skills/getting-started-with-directive/core-patterns.md +240 -0
- package/skills/getting-started-with-directive/examples.md +87 -0
- package/skills/getting-started-with-directive/sitemap.md +202 -0
- package/skills/hardening-ai-systems/SKILL.md +377 -0
- package/skills/hardening-ai-systems/ai-budget-resilience.md +408 -0
- package/skills/hardening-ai-systems/ai-guardrails-memory.md +370 -0
- package/skills/hardening-ai-systems/ai-security.md +400 -0
- package/skills/hardening-ai-systems/api-skeleton.md +5 -0
- package/skills/hardening-ai-systems/examples.md +628 -0
- package/skills/migrating-to-directive/SKILL.md +249 -0
- package/skills/migrating-to-directive/anti-patterns.md +382 -0
- package/skills/migrating-to-directive/api-skeleton.md +5 -0
- package/skills/migrating-to-directive/core-patterns.md +240 -0
- package/skills/migrating-to-directive/examples.md +511 -0
- package/skills/migrating-to-directive/schema-types.md +273 -0
- package/skills/reviewing-directive-code/SKILL.md +232 -0
- package/skills/reviewing-directive-code/anti-patterns.md +382 -0
- package/skills/reviewing-directive-code/api-skeleton.md +5 -0
- package/skills/reviewing-directive-code/core-patterns.md +240 -0
- package/skills/reviewing-directive-code/examples.md +463 -0
- package/skills/reviewing-directive-code/naming.md +321 -0
- package/skills/scaffolding-directive-modules/SKILL.md +218 -0
- package/skills/scaffolding-directive-modules/api-skeleton.md +5 -0
- package/skills/scaffolding-directive-modules/core-patterns.md +240 -0
- package/skills/scaffolding-directive-modules/examples.md +1386 -0
- package/skills/scaffolding-directive-modules/naming.md +321 -0
- package/skills/scaffolding-directive-modules/schema-types.md +273 -0
- package/skills/testing-ai-systems/SKILL.md +398 -0
- package/skills/testing-ai-systems/ai-debug-observability.md +329 -0
- package/skills/testing-ai-systems/ai-mcp-rag.md +293 -0
- package/skills/testing-ai-systems/ai-testing-evals.md +485 -0
- package/skills/testing-ai-systems/api-skeleton.md +5 -0
- package/skills/testing-ai-systems/examples.md +1261 -0
- package/skills/testing-directive-code/SKILL.md +479 -0
- package/skills/testing-directive-code/api-skeleton.md +5 -0
- package/skills/testing-directive-code/examples.md +156 -0
- package/skills/testing-directive-code/history.md +351 -0
- package/skills/testing-directive-code/testing.md +412 -0
- package/skills/writing-directive-constraints/SKILL.md +486 -0
- package/skills/writing-directive-constraints/api-skeleton.md +5 -0
- package/skills/writing-directive-constraints/constraints.md +274 -0
- package/skills/writing-directive-constraints/error-boundaries.md +330 -0
- package/skills/writing-directive-constraints/examples.md +1361 -0
- package/skills/writing-directive-constraints/resolvers.md +367 -0
- package/skills/writing-directive-modules/SKILL.md +342 -0
- package/skills/writing-directive-modules/anti-patterns.md +382 -0
- package/skills/writing-directive-modules/api-skeleton.md +5 -0
- package/skills/writing-directive-modules/core-patterns.md +240 -0
- package/skills/writing-directive-modules/examples.md +1639 -0
- package/skills/writing-directive-modules/naming.md +321 -0
- package/skills/writing-directive-modules/schema-types.md +273 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# React Adapter
|
|
2
|
+
|
|
3
|
+
> Covers `@directive-run/react` — `useFact`, `useDerived`, `useEvents`, `useSelector`, `useDirective`, `createDirectiveContext`.
|
|
4
|
+
|
|
5
|
+
The React adapter connects Directive systems to React components. Import from `@directive-run/react`.
|
|
6
|
+
|
|
7
|
+
## Canonical pattern: `createDirectiveContext`
|
|
8
|
+
|
|
9
|
+
This is the recommended way to use Directive in React. One call returns a typed `Provider` plus bound hooks — no system arg needed at every call site, no `useContext` boilerplate, full type inference from the schema.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// counter-context.ts — create once, import everywhere
|
|
13
|
+
import { createSystem } from "@directive-run/core";
|
|
14
|
+
import { createDirectiveContext } from "@directive-run/react";
|
|
15
|
+
import { counterModule } from "./counter-module";
|
|
16
|
+
|
|
17
|
+
export const counterSystem = createSystem({ module: counterModule });
|
|
18
|
+
export const Counter = createDirectiveContext(counterSystem);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
// App.tsx
|
|
23
|
+
import { Counter } from "./counter-context";
|
|
24
|
+
import { Display } from "./Display";
|
|
25
|
+
|
|
26
|
+
export function App() {
|
|
27
|
+
return (
|
|
28
|
+
<Counter.Provider>
|
|
29
|
+
<Display />
|
|
30
|
+
</Counter.Provider>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// Display.tsx
|
|
37
|
+
import { Counter } from "./counter-context";
|
|
38
|
+
|
|
39
|
+
export function Display() {
|
|
40
|
+
const count = Counter.useFact("count");
|
|
41
|
+
const doubled = Counter.useDerived("doubled");
|
|
42
|
+
const events = Counter.useEvents();
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div>
|
|
46
|
+
<p>Count: {count} (doubled: {doubled})</p>
|
|
47
|
+
<button onClick={() => events.increment()}>+1</button>
|
|
48
|
+
<button onClick={() => events.reset()}>Reset</button>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`createDirectiveContext(system)` returns: `{ Provider, useSystem, useFact, useDerived, useEvents, useDispatch, useSelector, useWatch, useInspect, useExplain, useHistory }`. All bound to the typed system — no system argument needed when calling them.
|
|
55
|
+
|
|
56
|
+
The Provider accepts an optional `system` prop override for testing:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<Counter.Provider system={testSystem}>
|
|
60
|
+
<ComponentUnderTest />
|
|
61
|
+
</Counter.Provider>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Standalone hooks (without a context)
|
|
65
|
+
|
|
66
|
+
When you don't want a context (e.g. one-off components, prototypes), import the hooks directly and pass the system to each call:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// system.ts
|
|
70
|
+
import { createSystem } from "@directive-run/core";
|
|
71
|
+
import { counterModule } from "./counter-module";
|
|
72
|
+
|
|
73
|
+
export const system = createSystem({ module: counterModule });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
// Counter.tsx
|
|
78
|
+
import { useFact, useDerived, useEvents } from "@directive-run/react";
|
|
79
|
+
import { system } from "./system";
|
|
80
|
+
|
|
81
|
+
export function Counter() {
|
|
82
|
+
const count = useFact(system, "count");
|
|
83
|
+
const doubled = useDerived(system, "doubled");
|
|
84
|
+
const events = useEvents(system);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div>
|
|
88
|
+
<p>Count: {count} (doubled: {doubled})</p>
|
|
89
|
+
<button onClick={() => events.increment()}>+1</button>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Hook reference
|
|
96
|
+
|
|
97
|
+
### `useFact(system, key)` or `useFact(system, [keys])`
|
|
98
|
+
|
|
99
|
+
Reads one fact (single key) or many facts (array of keys). Re-renders only when the selected fact(s) change. Return type is narrowed by the schema.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { useFact } from "@directive-run/react";
|
|
103
|
+
|
|
104
|
+
function Profile() {
|
|
105
|
+
// Single key — typed: name is string | undefined
|
|
106
|
+
const name = useFact(system, "name");
|
|
107
|
+
|
|
108
|
+
// Multi-key — typed: { name: string; age: number }
|
|
109
|
+
const { name, age } = useFact(system, ["name", "age"]);
|
|
110
|
+
|
|
111
|
+
return <div>{name} ({age})</div>;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `useDerived(system, id)` or `useDerived(system, [ids])`
|
|
116
|
+
|
|
117
|
+
Same shape as `useFact` but for derivations. Re-renders only when the derivation's value changes.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { useDerived } from "@directive-run/react";
|
|
121
|
+
|
|
122
|
+
function Status() {
|
|
123
|
+
const isReady = useDerived(system, "isReady");
|
|
124
|
+
const { isReady, isLoading } = useDerived(system, ["isReady", "isLoading"]);
|
|
125
|
+
|
|
126
|
+
return <div>{isReady ? "ready" : "loading"}</div>;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `useEvents(system)`
|
|
131
|
+
|
|
132
|
+
Returns the system's typed events accessor. Stable identity — does not cause re-renders. **This is the primary way to dispatch events from a component** — it carries autocomplete for every event name and payload shape.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { useEvents } from "@directive-run/react";
|
|
136
|
+
|
|
137
|
+
function CartActions() {
|
|
138
|
+
const events = useEvents(system);
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<>
|
|
142
|
+
<button onClick={() => events.addItem({ productId: "p1" })}>Add</button>
|
|
143
|
+
<button onClick={() => events.checkout()}>Checkout</button>
|
|
144
|
+
</>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Namespaced (multi-module) system — events are nested by module name
|
|
149
|
+
function Header() {
|
|
150
|
+
const events = useEvents(namespacedSystem);
|
|
151
|
+
|
|
152
|
+
return <button onClick={() => events.cart.addItem({ productId: "p1" })}>Add</button>;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `useSelector(system, selector, equalityFn?)`
|
|
157
|
+
|
|
158
|
+
For computed values across multiple facts and derivations. Auto-tracks accessed keys; re-renders only when the selected result changes (by `Object.is` equality, override with `equalityFn`).
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
import { useSelector, shallowEqual } from "@directive-run/react";
|
|
162
|
+
|
|
163
|
+
function CheckoutSummary() {
|
|
164
|
+
const summary = useSelector(system, (state) => ({
|
|
165
|
+
itemCount: state.items.length,
|
|
166
|
+
total: state.cartTotal,
|
|
167
|
+
isReady: state.canCheckout,
|
|
168
|
+
}), shallowEqual);
|
|
169
|
+
|
|
170
|
+
return <div>{summary.itemCount} items · {summary.total}</div>;
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`state` carries both facts and derivations in a flat namespace — that's why it's typed `InferSelectorState<S>`, not `InferFacts<S>`.
|
|
175
|
+
|
|
176
|
+
### `useDispatch(system)`
|
|
177
|
+
|
|
178
|
+
Returns a raw `dispatch(event)` function. Useful when you need to forward events programmatically or when the event name is computed at runtime. Prefer `useEvents` for normal dispatch — it carries typing.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { useDispatch } from "@directive-run/react";
|
|
182
|
+
|
|
183
|
+
function ToolBar({ actions }: { actions: SystemEvent[] }) {
|
|
184
|
+
const dispatch = useDispatch(system);
|
|
185
|
+
|
|
186
|
+
return actions.map((a) => (
|
|
187
|
+
<button key={a.type} onClick={() => dispatch(a)}>{a.type}</button>
|
|
188
|
+
));
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `useDirective(moduleOrOptions, selections?)`
|
|
193
|
+
|
|
194
|
+
Convenience hook that **creates a scoped system** AND selects facts/derivations in one call. The system lives for the lifetime of the component (created on mount, destroyed on unmount). Use this when the system's lifecycle should match a component's lifecycle — game boards, wizard flows, modal forms.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { useDirective } from "@directive-run/react";
|
|
198
|
+
import { gameModule } from "./game-module";
|
|
199
|
+
|
|
200
|
+
function GameBoard() {
|
|
201
|
+
// Selective subscription — only re-renders when score or isOver change
|
|
202
|
+
const { facts: { score }, derived: { isOver }, events } = useDirective(gameModule, {
|
|
203
|
+
facts: ["score"],
|
|
204
|
+
derived: ["isOver"],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<div>
|
|
209
|
+
<p>Score: {score}{isOver && " — game over!"}</p>
|
|
210
|
+
<button onClick={() => events.move({ direction: "up" })}>Up</button>
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Subscribe to everything (omit keys)
|
|
216
|
+
function Debug() {
|
|
217
|
+
const { facts, derived, events, dispatch } = useDirective(gameModule);
|
|
218
|
+
|
|
219
|
+
return <pre>{JSON.stringify({ facts, derived }, null, 2)}</pre>;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
For app-wide state, prefer `createDirectiveContext` over `useDirective` — the context pattern creates the system once instead of per-component.
|
|
224
|
+
|
|
225
|
+
### `useDirectiveRef(moduleOrOptions, config?)`
|
|
226
|
+
|
|
227
|
+
Just the system-lifecycle half of `useDirective`. Returns the `SingleModuleSystem<S>` instance. Use this when you need a scoped system without consuming its state in this component (e.g., you're going to thread it through props or a context).
|
|
228
|
+
|
|
229
|
+
### `useWatch(system, factOrDerivationId, callback)`
|
|
230
|
+
|
|
231
|
+
Imperative side-effect when a fact or derivation changes. Does NOT cause re-renders — use this for analytics, logging, focus management. For state you want to render, use `useFact` / `useDerived` / `useSelector`.
|
|
232
|
+
|
|
233
|
+
### Status + introspection hooks
|
|
234
|
+
|
|
235
|
+
| Hook | Purpose |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `useRequirementStatus(system, type)` | Reactive `idle / running / success / error` status for a requirement type |
|
|
238
|
+
| `useConstraintStatus(system, id)` | Reactive constraint enabled/disabled state |
|
|
239
|
+
| `useSuspenseRequirement(system, type)` | Same as `useRequirementStatus` but suspends until resolved (Suspense boundary required) |
|
|
240
|
+
| `useExplain(system, requirementId)` | Reactive English explanation of a requirement's `when` predicate |
|
|
241
|
+
| `useInspect(system, options?)` | Full system inspection state — constraints, requirements, resolvers, derivations |
|
|
242
|
+
| `useHistory(system)` | Time-travel UI helpers: `goBack`, `goForward`, `canUndo`, `canRedo`, snapshot list |
|
|
243
|
+
| `useAuditLedger(system)` | Subscribe to a configured `createAuditLedger` plugin's entries |
|
|
244
|
+
|
|
245
|
+
### SSR + hydration
|
|
246
|
+
|
|
247
|
+
`DirectiveHydrator` and `useHydratedSystem` close the SSR loop. See `system-api.md` → "Hydration" for the full pattern.
|
|
248
|
+
|
|
249
|
+
### Suspense for data fetching
|
|
250
|
+
|
|
251
|
+
`useQuerySystem` + `useSuspenseQuery` integrate `@directive-run/query`'s subscription/mutation primitives with React's Suspense and Error Boundaries. See the `@directive-run/query` docs.
|
|
252
|
+
|
|
253
|
+
## CRITICAL: hooks that DO NOT exist
|
|
254
|
+
|
|
255
|
+
LLMs frequently hallucinate these names because they sound plausible. None of them are exported from `@directive-run/react`.
|
|
256
|
+
|
|
257
|
+
| Hallucination | Use instead |
|
|
258
|
+
|---|---|
|
|
259
|
+
| `useEvent(system)` (singular) | `useEvents(system)` (plural) |
|
|
260
|
+
| `useSystem(config)` as a top-level import | `useDirective(module)` or `createDirectiveContext().useSystem` |
|
|
261
|
+
| `DirectiveProvider` as a top-level import | `createDirectiveContext(system).Provider` |
|
|
262
|
+
| `useDirectiveContext()` | `createDirectiveContext(system).useSystem()` |
|
|
263
|
+
| `useState`-style writes like `setCount(5)` | Dispatch events: `events.setCount(5)` (or whatever your module defines) |
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
// WRONG — useEvent does not exist
|
|
267
|
+
import { useEvent } from "@directive-run/react";
|
|
268
|
+
const events = useEvent(system);
|
|
269
|
+
|
|
270
|
+
// CORRECT — useEvents (plural)
|
|
271
|
+
import { useEvents } from "@directive-run/react";
|
|
272
|
+
const events = useEvents(system);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
// WRONG — DirectiveProvider is not a top-level export
|
|
277
|
+
import { DirectiveProvider, useDirectiveContext } from "@directive-run/react";
|
|
278
|
+
<DirectiveProvider system={system}>...</DirectiveProvider>
|
|
279
|
+
|
|
280
|
+
// CORRECT — Provider comes from createDirectiveContext()
|
|
281
|
+
import { createDirectiveContext } from "@directive-run/react";
|
|
282
|
+
export const Counter = createDirectiveContext(counterSystem);
|
|
283
|
+
<Counter.Provider>...</Counter.Provider>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Common mistakes
|
|
287
|
+
|
|
288
|
+
### Creating the system inside a component body
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
// WRONG — creates a new system on every render
|
|
292
|
+
function Counter() {
|
|
293
|
+
const system = createSystem({ module: counterModule });
|
|
294
|
+
const count = useFact(system, "count");
|
|
295
|
+
return <div>{count}</div>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// CORRECT — create outside the component
|
|
299
|
+
const system = createSystem({ module: counterModule });
|
|
300
|
+
|
|
301
|
+
function Counter() {
|
|
302
|
+
const count = useFact(system, "count");
|
|
303
|
+
return <div>{count}</div>;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ALSO CORRECT — useDirective manages lifecycle
|
|
307
|
+
function Counter() {
|
|
308
|
+
const { facts: { count } } = useDirective(counterModule, { facts: ["count"] });
|
|
309
|
+
return <div>{count}</div>;
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Selecting too much state (excess re-renders)
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
// WRONG — re-renders on ANY fact change
|
|
317
|
+
const allFacts = useSelector(system, (s) => s);
|
|
318
|
+
|
|
319
|
+
// WRONG — re-renders even when other facts change (new object identity every time)
|
|
320
|
+
const profile = useSelector(system, (s) => ({ name: s.name, age: s.age }));
|
|
321
|
+
|
|
322
|
+
// CORRECT — use the targeted hook
|
|
323
|
+
const name = useFact(system, "name");
|
|
324
|
+
|
|
325
|
+
// CORRECT — multi-key reads a stable subset
|
|
326
|
+
const { name, age } = useFact(system, ["name", "age"]);
|
|
327
|
+
|
|
328
|
+
// CORRECT — useSelector with shallowEqual for shaped output
|
|
329
|
+
import { shallowEqual } from "@directive-run/react";
|
|
330
|
+
const profile = useSelector(system, (s) => ({ name: s.name, age: s.age }), shallowEqual);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Mutating facts directly from an event handler
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
// WRONG — bypasses the event system, no audit trail, no constraint reaction
|
|
337
|
+
function Counter() {
|
|
338
|
+
const count = useFact(system, "count");
|
|
339
|
+
return <button onClick={() => { system.facts.count += 1; }}>{count}</button>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// CORRECT — dispatch an event
|
|
343
|
+
function Counter() {
|
|
344
|
+
const count = useFact(system, "count");
|
|
345
|
+
const events = useEvents(system);
|
|
346
|
+
return <button onClick={() => events.increment()}>{count}</button>;
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Casting values from hooks
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
// WRONG — types come from the schema
|
|
354
|
+
const profile = useFact(system, "profile") as UserProfile;
|
|
355
|
+
|
|
356
|
+
// CORRECT — the schema types it for you
|
|
357
|
+
const profile = useFact(system, "profile");
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Forgetting `useEvents` is stable
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
// UNNECESSARY — useEvents returns a stable reference, no useCallback needed
|
|
364
|
+
const events = useEvents(system);
|
|
365
|
+
const handleClick = useCallback(() => events.increment(), [events]);
|
|
366
|
+
|
|
367
|
+
// FINE — events.increment is stable across renders too
|
|
368
|
+
const events = useEvents(system);
|
|
369
|
+
<button onClick={() => events.increment()}>+</button>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## See also
|
|
373
|
+
|
|
374
|
+
- [`system-api.md`](./system-api.md) — the system instance the hooks here read from + the SSR/hydration story
|
|
375
|
+
- [`core-patterns.md`](./core-patterns.md) — the module shape every React example here assumes you've built
|
|
376
|
+
- [`anti-patterns.md`](./anti-patterns.md) — the hallucinated React hooks (`useEvent`, `useSystem`, `DirectiveProvider`) that AI assistants reach for
|