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
import { proxify } from "rxjs-proxify";
import { of } from "rxjs";
const source = of({ msg: 'Hello' }, { msg: 'World' });
const stream = proxify(source);
stream.msg.subscribe(console.log); // 'Hello', 'World'
proxify will create a Proxy for given ObservableThe package has good TypeScript support, so all props are intelli-sensed by cats, dogs, and IDEs:
import { of } from 'rxjs';
import { proxify } from 'rxjs-proxify';
const source = of({ a:1, b:1 }, { a:2, b:2 });
const stream = proxify(source);
stream. // <- will suggest .a .b .pipe .subscribe etc
t's also possible to call methods on values (even those using this keyword), e.g.:
import { proxify } from "rxjs-proxify";
import { of } from "rxjs";
const source = of({ msg: () => 'Hello' }, { msg: () => 'World' });
const stream = proxify(source);
// calls msg() fn on each value of the stream
stream.msg().subscribe(console.log); // 'Hello', 'World'
And you are still free to apply RxJS operators at any depth:
import { proxify } from "rxjs-proxify";
import { of } from "rxjs";
import { scan } from "rxjs/operators";
const source = of({ msg: 'Hello' }, { msg: 'World' });
const stream = proxify(source);
stream.msg.pipe(scan((a, c)=> a + c)).subscribe(console.log); // 'HelloWorld'
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.nextmethod?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
.nextmethod 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:thiswill be lost, not sure if that's a big dealI still have mixed feelings regarding whether pushing to state should be
a.b.c = 1ora.b.c.next(1)β latter is uglier, I admit, though it has two benefits:a.next({ b: { c: 1 } })a.b = 1is 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
applytrap and everything seems ok to me. Besides yourproxifydoes not keep the this of the root object if that's what you meanWhen it comes to
=vs.next: IMHO=with a company of.nextor 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 lostexample β I meant exactly that. Witho.f()I'd expectthisinf()to beo(as in objecto, not Observable ofo). Here's a test fromproxifythat 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 π.
proxifydoes 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
fncalls with wholethisissue 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'
autorundoes 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
autorunthing.combineLatestoperator 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 thinkruncould be better namedcomputedorderivedorautopipedπ¨π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
statething.Maybe even include it into
proxifylib (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)