pubkey / rxdb

A fast, local first, reactive Database for JavaScript Applications https://rxdb.info/
https://rxdb.info/
Apache License 2.0
21k stars 1.02k forks source link

what's the pattern for "connecting" components? #17

Closed faceyspacey closed 7 years ago

faceyspacey commented 7 years ago

It seems a redux-style connect API will soon come about so you don't have to manually call subscribe in componentDidMount etc. Are there any thoughts on this already so the wheel doesn't have to be re-invented? I.e. a strategy that you have in mind.

What about possibly a "transparent reactive programming" interface similar to what Meteor was originally doing and what MobX does. I.e. that you can can do this:

render() {
  return (
   <ul>
     {heroCollection
       .find()
       .sort('name')
       .$.map( docs => <li>{doc.name}</li)
      }
   </ul>
 )
}

A year ago I built https://github.com/ultimatejs/tracker-react to allow you to transparently reactive functions within your render method and made it so Meteor's Tracker tools were aware of any reactive calls within your render method and then called forceUpdate any time new data come through. An interface like that might also be attractive. I'm not sure how render in this case would be aware of any observable subscription updates. Actually, at least for a react class component it could be as simple and explicit as this:

render() {
  return (
   <ul>
     {heroCollection
       .find()
       .sort('name')
       .$
       .notifyComponent(this) //here's where the magic happens obviously
       .map( docs => <li>{doc.name}</li)
      }
   </ul>
 )
}

So essentially you're passing rxdb an instance of the component which it can intelligently call forceUpdate on when the subscription updates.

Having been a long-time Meteor advocate, i spent the last year completely getting off of it and using Redux instead. I'm not sure I personally would go back to transparent reactive programming as easy as it was. But that's mainly because for my projects I need the supreme testability of pureer functions. The above obviously has major side-effects. However, that depends on how you look at it. Right now we live in a world where because connections and getting data is so slow, we can't really think of our functions and components as a function of a global database--it's why Redux makes us jump through so many hoops to make your component tree a function of a local database, and then goes farther to impose on you that your components are pure through connect. However, you technically could in a reliable/testable way use data from your store in your components in a non-pure way (e.g. with the hypothetical api: store.get('key')), so long as the subscriptions notified the component to update similar to above. And then with fixtures for your client-side database you can easily test functions with the above side-effects in a reliable way, allowing you to think of your data as just constants. You won't be dealing with technically correct pure referentially transparent functions, but perhaps it doesn't matter--at least for testing where you can instantly mock your database, especially with rxdb.

So that's a case for a "referentially transparent reactive API" for rxdb. The major benefit of it all is that it definitely is way faster to at least prototype this code. I loved Meteor's Tracker + Blaze. I now love React + Redux. But I love the latter because of the reliability it gives me, certainly not for the speed of initially building things. It is at least 2x slower, even after mastering the tools. Basically the only case against a referentially transparent reactive interface is that it's not technically pure. There likely will be various testing tools you can't use (which rely on true pure functions), and leaks to the abstraction. I simply didn't want to invest the time to find out. I.e. do a complete comparison of testing essentially meteor's transparent reactive interface for React which I built with tracker-react vs. testing pure functions and redux. At the time i decided the best use of my time was just to get going with Redux et al. The ecosystem will definitely benefit the latter. However if I was newer to programming I'd definitely be far better off with the transparent reactive style. It's way way easier to grasp and get off to a running start with. So if you guys wanted to target that audience--which no doubt is large, perhaps larger than more senior programmers--you may have a first class solution. Because you're using observables, you may have already overcome some of the various problems Meteor's Tracker had. Here are 2 articles I wrote a year ago highlighting those problems:

https://medium.com/@faceyspacey/the-evolution-of-tracker-solutions-to-its-core-problems-4b9cb90d479a#.1yvdndyq5

https://medium.com/@faceyspacey/tracker-vs-redux-implicit-vs-explicit-df847abcc230#.syas78lpz

Observables may in fact be the way transparent reactive programming should be done. Basically Tracker re-runs your code too much and you have very little control over it. So it's inefficient in how it guarantees your views/subscriptions stay up to date. Observables likely re-runs way less. On the other hand, MobX is known for less component updates compared to Redux (at least prior to the latest react-redux 5.0) because of its spreadsheet-style computations where various dependencies between reactive functions/data is graphed and granularly updates leaves if one of its dependencies update. MobX's depdency graph is also what makes it way better than Tracker, solving many of the problems I highlighted about Tracker in the above articles. So, perhaps MobX is even more suited for the problem than Observables. At least more optimized for react than standalone observables, as MobX can take your entire app into consideration, not just ad-hoc observables used throughout your components, which may trigger a child update when the parent already updated. That's a problem that the recent 5.0 update to react-redux solves/improves.

The react-redux package just released some major changes in their 5.0 release. Basically it makes your connected components aware of its parents, and if it's parent updates, the children won't, whereas before each component was individually subscribe/connected to the redux store and might update even if it's parent was also subscribed and handled the update for both already. I assume that's still probably not as good as MobX, but in reality for 90% of most case is 90% as efficient. So that's one less point for MobX these days.

Anyway, just thought it might be useful to drop this information for you guys as it could be some good marketing opportunities for you.

Here's the original article on how MobX works:

https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.fgu8gpysi

faceyspacey commented 7 years ago

...another challenge I just realized: basically you don't want to create a new observable every time a component re-renders, unless the parameters to the observable change. So you'd need to detect that and re-use an existing observable or risk spawning many observables all calling forceUpdate on the component more than once. If that's a solveable problem, you should be good to go; otherwise the above ideas can be thrown in the trash :)

pubkey commented 7 years ago

Hi @faceyspacey

  1. I dont think RxDB should implement a fancy way to handle reacts component-bindings. When you create a reactive react-app you will need a way to handle your observables, with or without RxDB. So there must be a standard solution to use RxJS in react and we should not invent our own one.

  2. Sry but I neither know the term "transparent reactive programming" nor "referentially transparent reactive API". I read your medium-posts but could not find out why we need the things described there. Cant we just use our rxjs-"streams"? I never had a problem to fit them into redux-stores.

  3. Whats the problem with creating a new observable for every query? I mean its just a very simple javascript-object which does not do anything if not subscribed. If you want to keep the observables you can still store them anywhere outside of the component.

pubkey commented 7 years ago

@faceyspacey

knoopx commented 7 years ago

FYI: Connecting rxdb->mobx->react is easy with https://github.com/mobxjs/mobx-utils#fromstream