Effect.ts: Absence as First-Class

State ManagementLesson 49 of 51

30. Ref: update vs set

When to transform the current value vs. replace it completely

The Difference
const counter = yield* Ref.make(10);

// set: "I don't care what it was, make it 42"
yield* Ref.set(counter, 42);
// counter is now 42

// update: "Take what it is, and add 5"
yield* Ref.update(counter, n => n + 5);
// counter is now 47
Ref.set(ref, newValue)

Use when: You want to replace the value completely, ignoring what was there.

const status = yield* Ref.make("idle");

// User clicked "start"
yield* Ref.set(status, "running");

// Task finished
yield* Ref.set(status, "completed");

Pattern: New value doesn't depend on old value.

Ref.update(ref, fn)

Use when: You want to transform based on the current value.

const counter = yield* Ref.make(0);

// Increment
yield* Ref.update(counter, n => n + 1);

// Double it
yield* Ref.update(counter, n => n * 2);

// Add 10
yield* Ref.update(counter, n => n + 10);

Pattern: New value = fn(oldValue)

Real Example: Shopping Cart
const cart = Effect.gen(function* () {
  const items = yield* Ref.make<string[]>([]);

  // Add items (transform current array)
  yield* Ref.update(items, arr => [...arr, "apple"]);
  yield* Ref.update(items, arr => [...arr, "banana"]);
  yield* Ref.update(items, arr => [...arr, "orange"]);

  console.log(yield* Ref.get(items));
  // ["apple", "banana", "orange"]

  // Clear cart (replace with empty)
  yield* Ref.set(items, []);

  console.log(yield* Ref.get(items));
  // []
});
Common Patterns

Counters → use update

yield* Ref.update(count, n => n + 1);  // increment
yield* Ref.update(count, n => n - 1);  // decrement
yield* Ref.update(count, n => n * 2);  // double

Status/Flags → use set

yield* Ref.set(isLoading, true);
yield* Ref.set(status, "error");
yield* Ref.set(mode, "dark");

Arrays/Objects → use update

yield* Ref.update(todos, arr => [...arr, newTodo]);
yield* Ref.update(user, u => ({ ...u, age: 30 }));

Reset → use set

yield* Ref.set(cart, []);
yield* Ref.set(counter, 0);
yield* Ref.set(errors, null);
Quick Decision Tree
Do you need the current value?
├─ YES → use Ref.update()
└─ NO  → use Ref.set()
Bonus: updateAndGet

Want to update AND get the new value in one shot?

const newValue = yield* Ref.updateAndGet(counter, n => n + 1);
console.log(newValue); // New value after update

Saves you from doing:

yield* Ref.update(counter, n => n + 1);
const newValue = yield* Ref.get(counter);

Choose the right tool: set for replacement, update for transformation


Part 49 of 51 in the Effect.ts Absence Modeling series