Comments (3)
Thanks, as always, @allforabit! You're right, that snippet is indeed a typo and it should be fx.ROUTE_TO
. And I also hear you about this package direly needing some decent documentation, at least initially to just give a better overview of how these different parts fit together... Am currently deep diving at work and in my free time working out through different iterations devising guidelines for themeable components. Once that's more stable I will finally finish writing the 2nd mammoth blog post about this and probably can use parts of that for the interceptors readme too.
To briefly summarize the differences between event handlers & effects:
Event handlers are triggered by events, but each event handler is technically a chain of interceptors (even though many are just a single item). Even if you just specify a single function, it's internally translated into an array of interceptor objects like:
valueSetter("route") -> [{ pre: (...) => {[FX_STATE]: ...}, post: undefined }]
When processing an event, these interceptors are then executed first in ascending order for any pre
functions and then backwards again for any post
functions (only if there are any in the chain). So if you had defined an handler with this chain: [{pre: f1, post: f2}, {pre: f3}, {pre: f4, post: f5}]
, then the functions would be called in this order: f1, f3, f4, f5, f2
. The post
phase is largely intended for state/effect validation & logging post-update. Most interceptors I ever wrote are 95% using pre
only.
[UNDOABLE_EVENT]: [snapshot(), valueSetter("foo")]
The idea of interceptors is quite similar to functional composition and AOP (aspect oriented programming). E.g. if I have multiple actions which should be undoable, I can compose my main event handlers with the snapShot()
interceptor, which requires a @thi.ng/atom History
-like instance and records a snapshot of the current app state, but else is completely invisible. So yes, like with trace()
some interceptors DO have side effects (in fact these two are the only ones thus far), but they're really the exception from the rule and TBH not something I've ever worried about so far. Also snapshot()
is idempotent since it only records a new snapshot if it's different from the last and trace()
is usually only used during development and its side effect is outside the scope of your app (i.e. the console).
The idea of event handlers largely just being responsible to assign parameters to side effects, rather than executing effects themselves, is again mainly to do with the DRY-principle, instrumentation potential and performance. Most composed event handler chains are setup so that your "actual" main handler is last in line in the pre
processing phase. If e.g. your event handlers would directly update the state atom, then any attached watches (derived views, cursors, other subscriptions) would be re-run each time. By assigning the updated state to FX_STATE
we can avoid these interim updates and only apply the new state once all events in the current frame have been processed. Furthermore, a post
interceptor might cancel the event due to validation errors etc.
Also in most apps there're many more event types/handlers than possible result actions. So assigning them to registered side effects gives good reuse. Another aspect is debugging potential. With a break point set at the beginning of processEffects()
(in event-bus.ts
) I can see exactly which side effects have been caused each frame. That alone saved me nerves countless times... Of course at some point there should be a widget/component for that purpose as well.. :)
As for not allowing undefined
return values from interceptors: I'd say that's not necessary, but could be done if you think it helps with understanding. Though, would you want to force handlers to always require at least one side effect assignment? Else I could just return an empty object instead of undefined
.
Btw. I was going to add another version of trace()
, i.e. traceEffects()
to log the current accumulated side effect assignments and for that reason considered renaming trace()
to traceEvent()
. Objections? I can't currently see how a trace side effect would work, also in terms of chronological ordering. My main use case for trace()
so far was debugging the correct order of events fired (and re-fired/dispatched). For example in the app I'm currently working on there're dozens of high-level events whose only role is to re-dispatch several low-level events via FX_DISPATCH_NOW
. trace()
in its current form is very useful for that purpose.
from umbrella.
Thanks very much for this thorough explanation. It all makes a lot more sense now. In general, it's possible to get 99% of the way by reading the source code but this definitely fills in the final 1%! I can see now why the trace
is setup like that. I don't think there's really a problem with it only in that it was a bit of a curveball when using the source code to learn how the system works. I know I can treat that (and snapshot
) as the exception when it comes to interceptors. That sounds good about changing the name so that you can add more trace options. No need to worry about not allowing undefined
I think. One thing I was experimenting with was setting up an FX_NOOP
that could be returned when there's no side effects required although this might be overkill. Great tip on using the breakpoint at processEvents
. I can imagine it coming in very useful at some point. Thanks again for all the info!
from umbrella.
Curveballs are never good and am grateful to you pointing them out! Don't know if you already saw it, but I started adding Architecture Decision Records for the hdom-components package and might add some for the interceptors package too. I find the format quite helpful to flesh out & document new ideas...
Btw. The side effect inspection via breakpoint in processEffects()
is only a temporary until the traceEffects()
has landed.
from umbrella.
Related Issues (20)
- [geom] `withAttribs(path, {})` closes the path HOT 3
- [vectors] Vec2 missing x and y coordinate HOT 3
- [shader-ast] Bitwise shift operation HOT 3
- [shader-ast] Unsigned integer vector arithmetic HOT 1
- [shader-ast] Dependency for ternary operators, while and for loop HOT 1
- [shader-ast] Array texture types are missing HOT 1
- [shader-ast] Missing matrix types HOT 1
- [shader-ast] Better design for `defn` with anonymous qualified arguments HOT 7
- [malloc] Multi-threaded allocations HOT 4
- Prototype Pollution vulnerability affecting @thi.ng/paths, versions <=5.1.62 HOT 7
- [hiccup-canvas] "packedPolyline" needs separate implementation or packedPoly needs to accept stride options?
- [color] ? [binary] Comparison with -0 using the "===" operator will also match 0 [equals-negative-zero] HOT 2
- RDMS based on rstream-query HOT 1
- [examples] Issue running the examples (failed to resolve entry for package) HOT 3
- [hiccup-svg] hiccup-svg's attributes don't seem to handle rstream HOT 7
- [rstream] error when using skypack build HOT 5
- [geom-accel] SpatialGrid2 hangs runtime when using `set` method HOT 3
- Polygons with holes? HOT 9
- [shader-ast] "Building a shader graph editor (WebGL, shader AST transpiler, UI)" HOT 2
- [rdom] Issue understanding $klist HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from umbrella.