Skip to content

Commit 99ea01d

Browse files
committed
modify demos and system prompt to say SetFn is safe for modifications (no DeepCopy required)
1 parent af19bbf commit 99ea01d

3 files changed

Lines changed: 23 additions & 26 deletions

File tree

tsunami/demo/cpuchart/app.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,10 @@ var App = app.DefineComponent("App", func(_ struct{}) any {
147147
// Use UseTicker for continuous CPU data collection - automatically cleaned up on unmount
148148
app.UseTicker(time.Second, func() {
149149
// Collect new CPU data point and shift the data window
150-
cpuDataAtom.SetFn(func(currentData []CPUDataPoint) []CPUDataPoint {
150+
cpuDataAtom.SetFn(func(data []CPUDataPoint) []CPUDataPoint {
151151
newPoint := generateCPUDataPoint()
152152
currentDataPointCount := dataPointCountAtom.Get()
153153

154-
// Make a safe copy to avoid mutation issues
155-
data := app.DeepCopy(currentData)
156-
157154
// Ensure we have the right size array
158155
if len(data) != currentDataPointCount {
159156
// Resize array if config changed

tsunami/demo/todo/app.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ var App = app.DefineComponent("App", func(_ any) any {
112112
return
113113
}
114114
todosAtom.SetFn(func(todos []Todo) []Todo {
115-
newTodos := app.DeepCopy(todos)
116-
return append(newTodos, Todo{
115+
return append(todos, Todo{
117116
Id: nextIdAtom.Get(),
118117
Text: inputTextAtom.Get(),
119118
Completed: false,
@@ -126,14 +125,13 @@ var App = app.DefineComponent("App", func(_ any) any {
126125
// Immutable state update pattern
127126
toggleTodo := func(id int) {
128127
todosAtom.SetFn(func(todos []Todo) []Todo {
129-
newTodos := app.DeepCopy(todos)
130-
for i := range newTodos {
131-
if newTodos[i].Id == id {
132-
newTodos[i].Completed = !newTodos[i].Completed
128+
for i := range todos {
129+
if todos[i].Id == id {
130+
todos[i].Completed = !todos[i].Completed
133131
break
134132
}
135133
}
136-
return newTodos
134+
return todos
137135
})
138136
}
139137

tsunami/prompts/system.md

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -364,17 +364,18 @@ var MyComponent = app.DefineComponent("MyComponent", func(_ struct{}) any {
364364
})
365365
```
366366

367-
**Never mutate values from Get()**: For complex data types, never modify the value returned from `atom.Get()`. Always use `app.DeepCopy()` before mutations:
367+
**Never mutate values from Get()**: For complex data types, never modify the value returned from `atom.Get()`.
368+
369+
**Use SetFn() for safe mutations**: `SetFn()` automatically handles deep copying, making it safe to modify complex data:
368370

369371
```go
370372
var MyComponent = app.DefineComponent("MyComponent", func(_ struct{}) any {
371373
todos := app.UseLocal([]Todo{{Text: "Learn Tsunami"}})
372374

373375
addTodo := func() {
374-
// ✅ Correct: Copy before modifying
376+
// ✅ Correct: SetFn automatically deep copies the current value
375377
todos.SetFn(func(current []Todo) []Todo {
376-
todosCopy := app.DeepCopy(current)
377-
return append(todosCopy, Todo{Text: "New task"})
378+
return append(current, Todo{Text: "New task"})
378379
})
379380
}
380381

@@ -418,7 +419,8 @@ var MyComponent = app.DefineComponent("MyComponent", func(_ struct{}) any {
418419

419420
**Key Points:**
420421

421-
- Use `app.DeepCopy(value)` before modifying complex data from `atom.Get()`
422+
- `SetFn()` automatically deep copies the current value before passing it to your function
423+
- For direct mutations using `Set()`, manually use `app.DeepCopy(value)` before modifying complex data from `atom.Get()`
422424
- Always capture atoms in closures, never captured render values
423425
- This prevents stale closures and shared reference bugs
424426
- `app.DeepCopy[T any](value T) T` works with slices, maps, structs, and nested combinations
@@ -1009,7 +1011,7 @@ var DataPollerComponent = app.DefineComponent("DataPollerComponent", func(_ stru
10091011
status.Set("error")
10101012
} else {
10111013
data.SetFn(func(current []APIResult) []APIResult {
1012-
// Merge new data with existing, handle deduplication
1014+
// SetFn automatically deep copies current, safe to modify
10131015
return mergeResults(current, newData)
10141016
})
10151017
status.Set("success")
@@ -1073,8 +1075,8 @@ Atoms are internally synchronized, so multiple goroutines can safely call Get()
10731075
// Safe pattern for concurrent updates using SetFn
10741076
updateTodos := func() {
10751077
todosAtom.SetFn(func(current []Todo) []Todo {
1076-
todosCopy := app.DeepCopy(current)
1077-
return append(todosCopy, newTodo)
1078+
// SetFn automatically deep copies current value
1079+
return append(current, newTodo)
10781080
})
10791081
}
10801082
```
@@ -1172,7 +1174,7 @@ Key points:
11721174

11731175
- ✅ Read with atom.Get() in render code
11741176
- ❌ Never call atom.Set() in render code - only in handlers/effects
1175-
- ✅ Always use SetFn() for concurrent updates from goroutines
1177+
- ✅ Always use SetFn() for concurrent updates from goroutines (automatically deep copies the value)
11761178

11771179
## Tsunami App Template
11781180

@@ -1253,14 +1255,14 @@ var App = app.DefineComponent("App", func(_ struct{}) any {
12531255

12541256
toggleTodo := func(id int) {
12551257
todos.SetFn(func(current []Todo) []Todo {
1256-
todosCopy := app.DeepCopy(current)
1257-
for i := range todosCopy {
1258-
if todosCopy[i].Id == id {
1259-
todosCopy[i].Completed = !todosCopy[i].Completed
1258+
// SetFn automatically deep copies current value
1259+
for i := range current {
1260+
if current[i].Id == id {
1261+
current[i].Completed = !current[i].Completed
12601262
break
12611263
}
12621264
}
1263-
return todosCopy
1265+
return current
12641266
})
12651267
}
12661268

@@ -1330,7 +1332,7 @@ Key points:
13301332
2. **Missing keys in lists**: Always use `.WithKey(id)` for list items
13311333
3. **Stale closures in goroutines**: Use `atom.Get()` inside event handlers, effects, and goroutines, not captured values
13321334
4. **Wrong prop format**: Use `"className"` not `"class"`, `"onClick"` not `"onclick"` (matching React prop and style names)
1333-
5. **Mutating state**: Always create new slices/objects when updating atoms (can use app.DeepCopy helper)
1335+
5. **Mutating state**: With `SetFn()`, you can safely modify the current value as it's automatically deep copied. With `Set()`, create new slices/objects or use app.DeepCopy helper
13341336

13351337
## Styling Requirements
13361338

0 commit comments

Comments
 (0)