Skip to content

feat!: make listeners passive, implement all implicit events#158

Merged
bowheart merged 2 commits intomasterfrom
josh/finish-signals
Feb 2, 2025
Merged

feat!: make listeners passive, implement all implicit events#158
bowheart merged 2 commits intomasterfrom
josh/finish-signals

Conversation

@bowheart
Copy link
Collaborator

@bowheart bowheart commented Feb 2, 2025

@affects atoms, immer, machines, react, stores

Description

Introduce the concept of "passive" and "active" event listeners. Make listeners passive by default, which means they don't influence the node's lifecycle. Add the { active: true } ListenerConfig option for explicitly influencing lifecycle changes (this mimics the old instance.addDependent method - while the active listener is attached, it prevents automatic destruction. When removed, Zedux checks the ref count of the node and schedules destruction if needed).

Implicit Events

Finish implementing all implicit event types. They are officially:

  • change. Listeners receive a ChangeEvent with newState and oldState properties.
  • cycle. Listeners receive a CycleEvent with newStatus and oldStatus properties.
  • invalidate. Listeners receive an InvalidateEvent with no unique own properties (use event.source to access the invalidated node).
  • promiseChange. Listeners receive a PromiseChangeEvent with no unique own properties (use event.source.promise to access the new promise).

All implicit events also have these properties:

  operation?: string // will always be `"on"` for event listeners
  reasons?: EvaluationReason[]
  source?: GraphNode<G>
  type: string // matches the bullets above e.g. "change", "invalidate"

Explicit Events

Test and handle tons of edge cases around the mutate event. It's going to be much less common to attach listeners to inner signals than outer signals (or the whole atom) for relaying transactions e.g. to other realms or data grids. Because of this:

  • When mutating an outer signal, no mutate event is sent to inner signals - they weren't mutated, the outer signal was.
  • When mutating an inner signal, a mutate event is sent to the outer signal. This mutate event's transactions are modified to include the key path to the inner signal.

mutate is the only explicit event. batch is scrapped. It isn't possible to set a node's state and defer running its dependents. That was possible with stores since stores weren't graph nodes, but it still wasn't good - the store's state would be temporarily, visibly out of sync with its dependent atom until the scheduler ran on the next tick. This state tearing is not something we can allow in the atom graph at all - Zedux's atomic model is glitchless.

We could defer callback functions before allowing .set calls etc to update any node state. But it's such a rare use case and very easy for users to create a batchAtom that does that if needed - stores callback functions and runs them later. Since it's rarely used and easy to do manually or via an addon, Zedux should not include this functionality in its core.

Other Stuff

Finally(!) fix scheduler flushes in potentially recursive .set, .mutate, handleStateChange, .send calls. Introduce scheduler.pre and scheduler.post which function as essentially an ecosystem.batch() call without the need to create a closure. This fixed lots of new edge cases introduced with recursive mapped signals.

Guarantee listener call order based on graph node weight and order added. Inner signal listeners always run before outer signal listeners. Multiple listeners on the same node and event type (or different event types that are sent together) run in the order they were added in.

Add tests for complicated nesting configurations involving atoms and signals in other atoms and signals. Add tests for all implicit event types.

Issues

Resolves #115

@bowheart bowheart merged commit c71b4df into master Feb 2, 2025
2 checks passed
@bowheart bowheart deleted the josh/finish-signals branch February 2, 2025 21:25
@bowheart bowheart mentioned this pull request Feb 3, 2025
53 tasks
@bowheart bowheart added this to the Zedux v2 milestone Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New Signal Primitive

1 participant