Skip to content

Commit fdad7fd

Browse files
authored
Simplify charms-ref-in-cell.tsx (commontoolsinc#1800)
* Simplify charms-ref-in-cell.tsx Added TypeScript interfaces with toSchema() to have less JSONSchema template Improved comments Added random IDs to SimpleRecipe instances for uniqueness * Removed lift, replaced with Default * Removed the createCellRef lift * Added RecipeInOutput type with Default<any[], []> for the cellRef * Recipe now takes input/output parameters - the cellRef is passed in as a Default * Removed createCell import * use a slightly better type for charm than any
1 parent 2188232 commit fdad7fd

File tree

1 file changed

+77
-94
lines changed

1 file changed

+77
-94
lines changed

packages/patterns/charms-ref-in-cell.tsx

Lines changed: 77 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import {
33
Cell,
44
cell,
5-
createCell,
5+
Default,
66
derive,
77
h,
88
handler,
@@ -11,59 +11,36 @@ import {
1111
NAME,
1212
navigateTo,
1313
recipe,
14+
toSchema,
1415
UI,
1516
} from "commontools";
1617

17-
// the simple charm (to which we'll store references within a cell)
18-
const SimpleRecipe = recipe("Simple Recipe", () => ({
19-
[NAME]: "Some Simple Recipe",
20-
[UI]: <div>Some Simple Recipe</div>,
21-
}));
18+
type Charm = {
19+
[NAME]: string;
20+
[UI]: string;
21+
[key: string]: any;
22+
};
2223

23-
// Create a cell to store an array of charms
24-
const createCellRef = lift(
25-
{
26-
type: "object",
27-
properties: {
28-
isInitialized: { type: "boolean", default: false, asCell: true },
29-
storedCellRef: { type: "object", asCell: true },
30-
},
31-
},
32-
undefined,
33-
({ isInitialized, storedCellRef }) => {
34-
if (!isInitialized.get()) {
35-
console.log("Creating cellRef - first time");
36-
const newCellRef = createCell(undefined, "charmsArray");
37-
newCellRef.set([]);
38-
storedCellRef.set(newCellRef);
39-
isInitialized.set(true);
40-
return {
41-
cellRef: newCellRef,
42-
};
43-
} else {
44-
console.log("cellRef already initialized");
45-
}
46-
// If already initialized, return the stored cellRef
47-
return {
48-
cellRef: storedCellRef,
49-
};
50-
},
51-
);
24+
// Define interfaces for type safety
25+
interface AddCharmState {
26+
charm: any;
27+
cellRef: Cell<Charm[]>;
28+
isInitialized: Cell<boolean>;
29+
}
30+
const AddCharmSchema = toSchema<AddCharmState>();
5231

53-
// Add a charm to the array and navigate to it
54-
// we get a new isInitialized passed in for each
55-
// charm we add to the list. this makes sure
56-
// we only try to add the charm once to the list
57-
// and we only call navigateTo once
32+
// Simple charm that will be instantiated multiple times
33+
const SimpleRecipe = recipe<{ id: string }>("Simple Recipe", ({ id }) => ({
34+
[NAME]: derive(id, (idValue) => `SimpleRecipe: ${idValue}`),
35+
[UI]: <div>Simple Recipe id {id}</div>,
36+
}));
37+
38+
// Lift that adds a charm to the array and navigates to it.
39+
// The isInitialized flag prevents duplicate additions:
40+
// - Without it: lift runs → adds to array → array changes → lift runs again → duplicate
41+
// - With it: lift runs once → sets isInitialized → subsequent runs skip
5842
const addCharmAndNavigate = lift(
59-
{
60-
type: "object",
61-
properties: {
62-
charm: { type: "object" },
63-
cellRef: { type: "array", asCell: true },
64-
isInitialized: { type: "boolean", asCell: true },
65-
},
66-
},
43+
AddCharmSchema,
6744
undefined,
6845
({ charm, cellRef, isInitialized }) => {
6946
if (!isInitialized.get()) {
@@ -79,68 +56,74 @@ const addCharmAndNavigate = lift(
7956
},
8057
);
8158

82-
// Create a new SimpleRecipe and add it to the array
83-
const createSimpleRecipe = handler<unknown, { cellRef: Cell<any[]> }>(
59+
// Handler that creates a new charm instance and adds it to the array.
60+
// Each invocation creates its own isInitialized cell for tracking.
61+
const createSimpleRecipe = handler<unknown, { cellRef: Cell<Charm[]> }>(
8462
(_, { cellRef }) => {
8563
// Create isInitialized cell for this charm addition
8664
const isInitialized = cell(false);
8765

88-
// Create the charm
89-
const charm = SimpleRecipe({});
66+
// Create a random 5-digit ID
67+
const randomId = Math.floor(10000 + Math.random() * 90000).toString();
68+
69+
// Create the charm with unique ID
70+
const charm = SimpleRecipe({ id: randomId });
9071

9172
// Store the charm in the array and navigate
9273
return addCharmAndNavigate({ charm, cellRef, isInitialized });
9374
},
9475
);
9576

9677
// Handler to navigate to a specific charm from the list
97-
const goToCharm = handler<unknown, { charm: any }>(
78+
const goToCharm = handler<unknown, { charm: Charm }>(
9879
(_, { charm }) => {
9980
console.log("goToCharm clicked");
10081
return navigateTo(charm);
10182
},
10283
);
10384

104-
// create the named cell inside the recipe body, so we do it just once
105-
export default recipe("Charms Launcher", () => {
106-
// cell to store array of charms we created
107-
const { cellRef } = createCellRef({
108-
isInitialized: cell(false),
109-
storedCellRef: cell(),
110-
});
85+
// Recipe input/output type
86+
type RecipeInOutput = {
87+
cellRef: Default<Charm[], []>;
88+
};
11189

112-
return {
113-
[NAME]: "Charms Launcher",
114-
[UI]: (
115-
<div>
116-
<h3>Stored Charms:</h3>
117-
{ifElse(
118-
!cellRef?.length,
119-
<div>No charms created yet</div>,
120-
<ul>
121-
{cellRef.map((charm: any, index: number) => (
122-
<li>
123-
<ct-button
124-
onClick={goToCharm({ charm })}
125-
>
126-
Go to Charm {derive(index, (i) => i + 1)}
127-
</ct-button>
128-
<span>
129-
Charm {derive(index, (i) => i + 1)}:{" "}
130-
{charm[NAME] || "Unnamed"}
131-
</span>
132-
</li>
133-
))}
134-
</ul>,
135-
)}
90+
// Main recipe that manages an array of charm references
91+
export default recipe<RecipeInOutput, RecipeInOutput>(
92+
"Charms Launcher",
93+
({ cellRef }) => {
94+
return {
95+
[NAME]: "Charms Launcher",
96+
[UI]: (
97+
<div>
98+
<h3>Stored Charms:</h3>
99+
{ifElse(
100+
!cellRef?.length,
101+
<div>No charms created yet</div>,
102+
<ul>
103+
{cellRef.map((charm: any, index: number) => (
104+
<li>
105+
<ct-button
106+
onClick={goToCharm({ charm })}
107+
>
108+
Go to Charm {derive(index, (i) => i + 1)}
109+
</ct-button>
110+
<span>
111+
Charm {derive(index, (i) => i + 1)}:{" "}
112+
{charm[NAME] || "Unnamed"}
113+
</span>
114+
</li>
115+
))}
116+
</ul>,
117+
)}
136118

137-
<ct-button
138-
onClick={createSimpleRecipe({ cellRef })}
139-
>
140-
Create New Charm
141-
</ct-button>
142-
</div>
143-
),
144-
cellRef,
145-
};
146-
});
119+
<ct-button
120+
onClick={createSimpleRecipe({ cellRef })}
121+
>
122+
Create New Charm
123+
</ct-button>
124+
</div>
125+
),
126+
cellRef,
127+
};
128+
},
129+
);

0 commit comments

Comments
 (0)