Hi, fellow RxJS streamer! π
Today I want to share a JS/TS package that allows you to access props of objects on Observables:
source$.subscribe(o => console.log(o?.a?.b?.c))
// turn β into β
source$.a.b.c.subscribe(console.log)
tl;dr: github.com/kosich/rxjs-proxify
A simple use case: read the msg property of each value on the stream
The package has good TypeScript support, so all props are intelli-sensed by cats, dogs, and IDEs:
t's also possible to call methods on values (even those using this
keyword), e.g.:
And you are still free to apply RxJS operators at any depth:
The package uses Proxies under the hood, recursively applying it to sub-properties and method results, so the chain can be indefinitely deep. And you can apply .subscribe
or .pipe
at any time!
πΉ Try it
You can install it via npm i rxjs-proxify
Or test it online: stackblitz.com/edit/rxjs-proxify-repl
π Repository
The source code and more examples are available on github repo:
github.com/kosich/rxjs-proxify
Outro
Thank you for reading this article! Stay reactive and have a nice day π
If you enjoyed reading β please, indicate that with β€οΈ π¦ π buttons β it helps a lot!
Soon I'll post a more detailed review of the lib and how it works
Follow me here and on twitter for more RxJS, React, and JS posts:
π£ Would love to hear your thoughts!
Psst.. need something more to read?
I got you covered:
π
Cya π
Top comments (12)
Does it play nicely with the
<$>
component? It would be a great combination. Itβs a similar idea to Hookstate. And what aboutcats and dogsSubject
? and.next
method?Hi, Franciszek!
Great question! Especially since I haven't tried it yet with
<$>
myself! π (the idea came up for use in recksjs framework) Now I did! Here's an example:^ online playground β I've added two precautions in comments, please be advised!
Hm, I haven't considered this before π€ The original idea was to provide a selector, independently from provider. And the
.next
method would be lost after first subproperty access, e.g.stream.a
β since it'smap
-ped under the hood. Though having the initial Subject, one can still do:THEORETICAL PART
Yet there's something to be discovered w/ state management, like:
Quite interesting concept, need to think & play around w/ this! π€
EOF THEORETICAL PART
Let me know what you think!
P.S: And thx for Hookstate, didn't know about it!
THEORETICAL UPDATE:
~ Well typed, though has bugs π
Heres a playground: stackblitz.com/edit/rstate?file=in...
Wow, it looks like MobX now π€―. I checked the code. What about changing the approach? Instead of chaining observables, we can chain the properties names and then just pluck them for subscribing. I made the demo. I also used Immer to deliver next state π
Awesome! I especially like that you've united read & write: I wanted to do it too! (but reused rxjs-proxify cz of already implemented TypeScript)
And I agree, a single pluck is nicer & more performant, though we might need a custom pluck operator if we want to support
a.b.c()
β since pluck won't keep the call context:this
will be lost, not sure if that's a big dealI still have mixed feelings regarding whether pushing to state should be
a.b.c = 1
ora.b.c.next(1)
β latter is uglier, I admit, though it has two benefits:a.next({ b: { c: 1 } })
a.b = 1
is not obviously effectful)Regardless of implementation details, do you think such approach could be useful as a package? I think in Recks it can be handy, maybe in
<$>
too, not sure where else..Will try to compile a unified solution early next week π
I don't get the first point. Which this will be lost? Help me understand π . I updated the demo with
apply
trap and everything seems ok to me. Besides yourproxify
does not keep the this of the root object if that's what you meanWhen it comes to
=
vs.next
: IMHO=
with a company of.next
or just.next
. Keeping only=
as you just said disallows setting the root state and makes the proxy less usable in higher-order scenarios like passing the chain as a prop to further set the next value.Could it be handy as a package? Using RxJS as a standalone state manager is very rare these times. Subjects in comparison to their relatives from MobX seems poor. Although mixing MobX with RxJs feels a little cumbersome due to the very different subscription fashion. Maybe it would be better to create a store by nesting observables as MobX does?
Sorry, now I'm confused π I see that updated demo has the
"this" is lost
example β I meant exactly that. Witho.f()
I'd expectthis
inf()
to beo
(as in objecto
, not Observable ofo
). Here's a test fromproxify
that might better explain it.Yet, this is a really minor issue (if it is an issue in the first place!), easy to fix and not worth much attention.
Can you share an example of this? I might be missing something obvious here π
Will have to educate myself better on MobX to appreciate this (haven't worked with MobX yet, only tutorials π )
Give me a hint if you have some particular use case in mind π€
P.S: Thanks for
fn?.()
β I didn't know that optional chaining can be applied to function calls! That's great!Apologize for my wicked accusations π.
proxify
does keep thethis
. I didn't test it before just got the wrong claims reading the sourcePhew π ! That's cool! Thanks for proofreading the sources! π
Hey, @fkrasnowski , sorry for bothering you again π
Want to give an update:
State:
π listen to distinct state value changes
π write to state (sub)values
π« reset state
π sync read current state value
π TS support coming
/ I've dropped
fn
calls with wholethis
issue for now π and used your cool approach with pluck! π /π work in progress
Autorun:
Another THEORETICAL thing born in discussions (here and on twitter for the very same
proxify
π)A function that is re-evaluated when an Observable used in it changes
(I think MobX'
autorun
does a similar thing)π work in progress #2
Mix:
The two might look cool together:
Let me know what you think π€
Take care!
I've got mixed feelings about all this
autorun
thing.combineLatest
operator for the same purpose, less cool, still clear, and convenient.Comparison between some aproaches:
RxJS:
MobX:
Svelte:
It might look like MobX and Svelte strategy is better, due to their terseness. But RxJS is about operators! And its full force lays in them. Your solution:
Best of both worlds??
Glad you add
distinctUntilChanged
. And I thinkrun
could be better namedcomputed
orderived
orautopiped
π¨πTotally agree with all three points!
And I love your examples β this gives some perspective! Thanks π
I've started this only because it was a fun dev challenge, I was sure as an API it's useless and error-prone! Then when it was ready... I began to have doubts π€π
The shorter notation might make sense in templates like in
<$>
or Recks...Probably it's a maker's affection of some sort: when you're painting something for a day long, and you look at it -- and it's crap, you know it's crap but still you kinda like it π
I think, I'll finish and post the
state
thing.Maybe even include it into
proxify
lib (bad naming here too π€¦ββοΈ)Still not sure what to do w/
autorun
β will polish it, then we'll see...BTW, I've been sharing all this on twitter too, here's a thread
VΓctor Oliva made another cool autorun concept β check it out!
(I'd ping you long ago, though haven't found you there)
and here's latest concept w/ state & autorun from twitter:
Thanks for taking your time to look into this on Friday evening π
Have a good weekend!
SATURDAY: okay... I've shared autorun on github github.com/kosich/rxjs-autorun (no npm package, naming is still a problem)