Skip to content

Commit 15c491d

Browse files
jkomorosclaude
andcommitted
Document on-demand pattern creation pattern
Based on real-world experience building shopping list launcher, document the pattern of creating child patterns on-demand in handlers when they share parent cell references. Key learnings: - Dynamic pattern creation IS supported via handlers - "Shadow ref alias" error occurs when creating shared-cell patterns during recipe init instead of on-demand - Solution: Create patterns in handlers for conditional/user-selected views Proposed documentation updates: 1. PATTERNS.md: New section on upfront vs on-demand pattern creation 2. RECIPES.md: Add "Shadow ref alias" error to pitfalls section 3. New example: packages/patterns/on-demand-pattern-creation.tsx 4. INDEX.md: Add entry for new example This addresses a common confusion where developers assume dynamic pattern creation isn't supported, when in fact it's fully supported and preferred for conditional pattern instantiation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f889756 commit 15c491d

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed

docs-update-on-demand-patterns.md

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# Documentation Updates: On-Demand Pattern Creation
2+
3+
Based on real-world pattern development experience, these additions would help developers avoid common pitfalls.
4+
5+
## 1. Add to PATTERNS.md - Level 4: Pattern Composition
6+
7+
### New Section: "When to Create Patterns On-Demand"
8+
9+
**Location**: After the existing Level 4 pattern composition example (around line 356)
10+
11+
**Content**:
12+
13+
```markdown
14+
### Pattern Composition: Upfront vs On-Demand Creation
15+
16+
When composing patterns that share cell references, you have two approaches:
17+
18+
#### ✅ Upfront Creation (All patterns rendered together)
19+
20+
```typescript
21+
export default recipe("Multi-View", ({ items }) => {
22+
// Create all patterns upfront
23+
const listView = ShoppingList({ items });
24+
const gridView = GridView({ items });
25+
26+
return {
27+
[NAME]: "Multi-View",
28+
[UI]: (
29+
<div>
30+
{/* Both patterns always rendered */}
31+
<div>{listView}</div>
32+
<div>{gridView}</div>
33+
</div>
34+
),
35+
items,
36+
};
37+
});
38+
```
39+
40+
**Use when**: All child patterns are displayed simultaneously or conditionally rendered with `ifElse()`.
41+
42+
#### ✅ On-Demand Creation (Patterns created when needed)
43+
44+
```typescript
45+
const selectView = handler<
46+
unknown,
47+
{ currentView: Cell<any>; items: any; viewType: string }
48+
>((_event, { currentView, items, viewType }) => {
49+
// Create pattern on-demand in handler
50+
const view = viewType === "list"
51+
? ShoppingList({ items })
52+
: GridView({ items });
53+
54+
currentView.set(view);
55+
});
56+
57+
export default recipe("View Selector", ({ items }) => {
58+
const currentView = cell<any>(null);
59+
60+
return {
61+
[NAME]: "View Selector",
62+
[UI]: (
63+
<div>
64+
<ct-button onClick={selectView({ currentView, items, viewType: "list" })}>
65+
List View
66+
</ct-button>
67+
<ct-button onClick={selectView({ currentView, items, viewType: "grid" })}>
68+
Grid View
69+
</ct-button>
70+
71+
{ifElse(
72+
derive(currentView, (v) => v !== null),
73+
<div>{currentView}</div>,
74+
<div />
75+
)}
76+
</div>
77+
),
78+
items,
79+
};
80+
});
81+
```
82+
83+
**Use when**: Child patterns are created based on user selection or other runtime conditions.
84+
85+
#### Why This Matters
86+
87+
Creating patterns that share parent cells during recipe initialization can cause cell tracking issues when those patterns are conditionally instantiated. The framework's cell system tracks references during pattern creation - creating patterns on-demand in handlers ensures proper reference tracking.
88+
89+
**Common Error**: If you see "Shadow ref alias with parent cell not found in current frame", you're likely creating shared-cell child patterns during recipe init when they should be created on-demand.
90+
91+
**Rule of thumb**:
92+
- Multiple views always visible → Create upfront
93+
- User selects which view → Create on-demand in handler
94+
```
95+
96+
## 2. Add to RECIPES.md - Common Pitfalls Section
97+
98+
**Location**: Near the existing pitfalls section (around line 450)
99+
100+
**Content**:
101+
102+
```markdown
103+
### Pitfall: "Shadow ref alias" Error with Pattern Composition
104+
105+
**Error**: `Shadow ref alias with parent cell not found in current frame`
106+
107+
**Cause**: Creating child patterns that share parent cell references during recipe initialization, when those patterns should be created conditionally.
108+
109+
**Wrong**:
110+
```typescript
111+
export default recipe("Launcher", ({ items }) => {
112+
const currentView = cell("none");
113+
114+
// ❌ Creating patterns upfront when they'll be conditionally used
115+
const listView = ShoppingList({ items });
116+
const gridView = GridView({ items });
117+
118+
// Later: conditionally show based on user selection...
119+
});
120+
```
121+
122+
**Right**:
123+
```typescript
124+
const selectList = handler((_event, { items, currentView }) => {
125+
// ✅ Create pattern on-demand
126+
const view = ShoppingList({ items });
127+
currentView.set(view);
128+
});
129+
130+
export default recipe("Launcher", ({ items }) => {
131+
const currentView = cell(null);
132+
133+
return {
134+
[UI]: (
135+
<ct-button onClick={selectList({ items, currentView })}>
136+
Show List
137+
</ct-button>
138+
),
139+
};
140+
});
141+
```
142+
143+
**When this applies**: Only when child patterns share cell references with parent AND are created conditionally based on user interaction.
144+
```
145+
146+
## 3. Add Example to packages/patterns/
147+
148+
**New file**: `packages/patterns/on-demand-pattern-creation.tsx`
149+
150+
```typescript
151+
/// <cts-enable />
152+
import { cell, Cell, Default, derive, handler, ifElse, NAME, recipe, UI } from "commontools";
153+
154+
interface Item {
155+
title: string;
156+
done: Default<boolean, false>;
157+
}
158+
159+
// Simple list view
160+
const ListView = recipe<{ items: Default<Item[], []> }>(
161+
"List View",
162+
({ items }) => ({
163+
[NAME]: "List View",
164+
[UI]: (
165+
<div>
166+
{items.map((item) => (
167+
<div>• {item.title}</div>
168+
))}
169+
</div>
170+
),
171+
items,
172+
})
173+
);
174+
175+
// Grid view
176+
const GridView = recipe<{ items: Default<Item[], []> }>(
177+
"Grid View",
178+
({ items }) => ({
179+
[NAME]: "Grid View",
180+
[UI]: (
181+
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
182+
{items.map((item) => (
183+
<div style={{ border: "1px solid gray", padding: "8px" }}>
184+
{item.title}
185+
</div>
186+
))}
187+
</div>
188+
),
189+
items,
190+
})
191+
);
192+
193+
// Handler that creates patterns on-demand
194+
const selectView = handler<
195+
unknown,
196+
{ currentView: Cell<any>; items: any; viewType: string }
197+
>((_event, { currentView, items, viewType }) => {
198+
const view = viewType === "list"
199+
? ListView({ items })
200+
: GridView({ items });
201+
202+
currentView.set(view);
203+
});
204+
205+
export default recipe<{ items: Default<Item[], []> }>(
206+
"On-Demand Pattern Example",
207+
({ items }) => {
208+
const currentView = cell<any>(null);
209+
const hasView = derive(currentView, (v) => v !== null);
210+
211+
return {
212+
[NAME]: "View Selector",
213+
[UI]: (
214+
<div style={{ padding: "1rem" }}>
215+
<h3>Select a View</h3>
216+
217+
<div style={{ display: "flex", gap: "8px", marginBottom: "16px" }}>
218+
<ct-button onClick={selectView({ currentView, items, viewType: "list" })}>
219+
List View
220+
</ct-button>
221+
<ct-button onClick={selectView({ currentView, items, viewType: "grid" })}>
222+
Grid View
223+
</ct-button>
224+
</div>
225+
226+
{ifElse(
227+
hasView,
228+
<div>{currentView}</div>,
229+
<div style={{ color: "#666" }}>Choose a view above</div>
230+
)}
231+
</div>
232+
),
233+
items,
234+
};
235+
}
236+
);
237+
```
238+
239+
## 4. Update packages/patterns/INDEX.md
240+
241+
Add entry for the new example:
242+
243+
```markdown
244+
- **on-demand-pattern-creation.tsx**: Demonstrates creating child patterns on-demand in handlers when they share parent cell references. Shows the difference between upfront and conditional pattern creation. (Keywords: composition, handlers, conditional, dynamic)
245+
```
246+
247+
## Summary
248+
249+
These documentation updates would:
250+
251+
1. **Explain the pattern**: When and why to create patterns in handlers vs recipe init
252+
2. **Document the error**: Help developers recognize and fix "Shadow ref alias" errors
253+
3. **Provide examples**: Show working code for both approaches
254+
4. **Establish guidelines**: Clear rules for when to use each approach
255+
256+
The core insight: **Dynamic pattern creation is fully supported - use handlers for conditional/on-demand creation of patterns that share parent cells.**

0 commit comments

Comments
 (0)