loading...
Cover image for RxJS Proxy: 3 new features
RxJS

RxJS Proxy: 3 new features

kosich profile image Kostia Palchyk ・3 min read

amazing header photo by Zach Kadolph

Hi! πŸ‘‹ In the previous post, I've introduced you to rxjs-proxify that turns your Observable into an Object of Observables:

const o = of({ msg: 'Hello' }, { msg: 'World' });
const p = proxify(o);
p.msg.subscribe(console.log); // > Hello > World
Enter fullscreen mode Exit fullscreen mode

Today I'll show you what new abilities you get with the recent 0.0.10 release: now proxify can be applied not only on Observables but also on Subjects and BehaviorSubjects. Also, we get a statify method that will help you manage the local state!

tl;dr try & install: github.com/kosich/rxjs-proxify

πŸ“– Intro

Observables, Subjects, and BehaviorSubjects

Left to right: Observable, Subject, and BehaviorSubject. Cute photo by The Lucky Neko.

Observable Proxy (already familiar to you)
subscribe at any depth

const observable = proxify( of({ p: 'πŸ‘' }) );
observable.subscribe(console.log); // > { p: πŸ‘ }
observable.p.subscribe(console.log); // > πŸ‘
Enter fullscreen mode Exit fullscreen mode

It will be handy if you need to access many sub-properties on your streams:

// framework agnostic model
const { title, body } = proxify( fetchArticle() );
// framework agnostic view
<article>
  <Title value={ title } />
  <Body value={ body } />
</article>
Enter fullscreen mode Exit fullscreen mode

Note that here we're creating two separate Observables, so you'll probably want to .pipe( share() ) inside fetchArticle.


Subject Proxy
subscribe at any depth, push at the root

const subject = proxify(new Subject<{ p: string }>());
subject.subscribe(console.log);
subject.p.subscribe(console.log);
subject.next({ p: 'πŸ₯' }); // > { p: πŸ₯ } // > πŸ₯
Enter fullscreen mode Exit fullscreen mode

While you have all the benefits of Observable Proxy, you can also update the whole Subject value.


BehaviorSubject Proxy

subscribe at any depth, push at any depth, synchronously read the current state

const behavior = proxify(new BehaviorSubject({ p: 'πŸ–' }));
behavior.p.subscribe(console.log); // > πŸ–
behavior.p.next('πŸ‡'); // > πŸ‡
console.log(behavior.p.value) // > πŸ‡
Enter fullscreen mode Exit fullscreen mode

In addition to Observable Proxy, you can synchronously read and update the state at any level!

// model
const s = proxify(
  new BehaviorSubject({ title: '', checked: false })
);
// view
<div>
  <input
    value={ s.title }
    onChange={ e => s.title.next(e.target.value) } />
  <input
    type="checkbox"
    checked={ s.checked }
    onChange={ e => s.checked.next(e.target.checked) } />
</div>
Enter fullscreen mode Exit fullscreen mode

State proxy

And we also export a statify function that creates a BehaviorSubject Proxy with a distinctUntilChanged on all its properties:

// create a state
const state = statify({ a: '🐰', z: '🏑' });

// listen to & log root state changes
state.subscribe(console.log); //> { a:🐰 z:🏑 }

// update particular substate
state.a.next('πŸ‡'); //> { a:πŸ‡ z:🏑 }
state.a.next('πŸ‡'); //> same value, no update

// read current values
console.log(state.z.value + state.a.value); //> πŸ‘πŸ‡

// update root state, still logging
state.next({ a: 'πŸ‡', z: '☁️' }) //> { a:πŸ‡ z:☁️ }

// and then…
state.z.next('πŸŒ™');   //> { a:πŸ‡  z:πŸŒ™ }
state.a.next('πŸ‡πŸ‘€'); //> { a:πŸ‡πŸ‘€ z:πŸŒ™ }
state.z.next('πŸ›Έ')    //> { a:πŸ‡πŸ‘€ z:πŸ›Έ }
state.a.next('πŸ’¨');   //> { a:πŸ’¨  z:πŸ›Έ }
Enter fullscreen mode Exit fullscreen mode

That's it! Just a few words before you go:

πŸŽ‰ Outro

You can try proxify online.

And you'll find more examples and the installation guide at github.com/kosich/rxjs-proxify. Hope you'll find these little tools useful!

If you enjoyed reading β€” please, indicate that with ❀️ πŸ¦„ πŸ“˜ buttons

Follow me here and on twitter for more RxJS, React, and JS posts!

Thank you for reading this article! Stay reactive and have a nice day πŸ™‚

And thanks to @fkrasnowski for discussing with me and polishing this idea in the previous post comments!

Discussion

pic
Editor guide
Collapse
t7yang profile image
t7yang
<input
  value={ s.title }
  onChange={ e => s.title.next(e.target.value) } />
Enter fullscreen mode Exit fullscreen mode

How possible to pass s.title directly into input value πŸ€”
I try this in codesandbox, it did throwing an error with invalid value prop value on <input> tag.

Collapse
kosich profile image
Kostia Palchyk Author

Hey, t7yang! A totally valid question πŸ˜…

I wanted to show the idea w/o binding to any framework, so the code is somwehwat pseudocodish: some frameworks need adaptations for RxJS (regardless of whether we have a proxy or not)

I haven't studied how to adapt RxJS to other frameworks, but you got a general idea.

Please, ping me if you have any questions left
πŸ‘‹

Collapse
t7yang profile image
t7yang

Got it. I am Angular developer, async pipe should work well with this.

ReckJS and <$> pretty interesting. I saw ReckJS before, cool πŸ₯³

Thread Thread
kosich profile image
Kostia Palchyk Author

Thanks, I'm glad you enjoyed it πŸ™Œ

Ah, yeah, Angular is very powerful! And don't forget about the onPush change detection strategy! πŸ™‚

Collapse
fkrasnowski profile image
Franciszek Krasnowski

Great job!. This package is pure joy 🀩. I find it a good choice for prototyping due to smooth DX. Glad you thought about proxifying the .pipe method:

 // we should wrap piped observable into another proxy
return function () {
    const applied = Reflect.apply(builtIn, deepO, arguments);
    return coreProxy(applied);
};
Enter fullscreen mode Exit fullscreen mode

It's something I didn't think about for the first time

Collapse
kosich profile image
Kostia Palchyk Author

Ah, I'm very glad you enjoyed this update, Franciszek!

Yeah, recursive proxying together with fn calls is crazy:

const a = of([1, 2, 3]);
const b = of([4, 5, 6]);
proxify(a)
  .reverse()
  .pipe( merge(b) )
  .reduce((a, c) => a + c, '')
  .subscribe(console.log);
// > 321
// > 456
Enter fullscreen mode Exit fullscreen mode

I bet it's not practical. Just πŸ€ͺ

Also, a weird thing I've noticed: ideas just happen to naturally pop up while exploring. I'll share some rxjs-autorun tricks this week β€” so far, two features we just discovered, not intentionally invented! 🀯

Collapse
fkrasnowski profile image
Franciszek Krasnowski

It's fun in a good way. A lot of libraries use Proxy. So it's not some crazy niche. For example I like the pipeline operator:

of('❀')
  |> mergeMap(heart => of( 'πŸ’',  'πŸ¦†',  'πŸ‹' )
     |> map(animal => heart + animal ))
  |> subscribe(console.log)
//β€πŸ’ 
//β€πŸ¦† 
//β€πŸ‹ 
Enter fullscreen mode Exit fullscreen mode

Not a big change but it's not standardized.
Should I hold my horses or go for it and show code like that to others?

Thread Thread
kosich profile image
Kostia Palchyk Author

Pipeline looks promising πŸ‘
I was so fascinated that added |> to my RxJS playground: thinkrx.io/rxjs/js-pipeline/ , but with a lot of warning signs ⚠️

I'm not sure that we can openly use it for teaching or openly speaking about RxJS, probably just among those who we know wouldn't be confused by it. In the near future, at least.

--

Btw, about combining strings, this came out of rxjs-autorun (not my idea):

 // should be async to capture all 3
const animals = of( 'πŸ’',  'πŸ¦†',  'πŸ‹' ).pipe(delay(1));
tracktag`❀ ${ animals }`
  .subscribe(console.log);
//β€πŸ’ 
//β€πŸ¦† 
//β€πŸ‹ 
Enter fullscreen mode Exit fullscreen mode

Basically, tracktag turns a template string into a computed expression from autorun.

Try it at stackblitz.com/edit/rxjs-autorun-t...

--

Which kinda automatically lead to HTML tags:

HTML tag combination

Try it at stackblitz.com/edit/rxjs-autorun-h...

--

Sorry for spamming you again with a million links. It's because you're among the few with whom I can share it πŸ€·β€β™‚οΈ