vigetlabs / microcosm

Flux with actions at center stage. Write optimistic updates, cancel requests, and track changes with ease.
http://code.viget.com/microcosm/
MIT License
487 stars 22 forks source link

Thoughts on a getDataBindings method for React components? #43

Closed briandipalma closed 9 years ago

briandipalma commented 9 years ago

I was wondering if you'd thought of providing finer grained subscriptions to data change events? As it stands the current approach

let app = new Microcosm()

// Add a callback
app.listen(callback)

would require a complete render of your entire application from the root React component. This works for simple applications but once you start buildings larger applications or have a high frequency of change events it will struggle.

Have you seen how nuclear-js handles this case? It has a getDataBindings method on the React components that specify what the components are interested in. This is similar to GraphQL.

The approach of having view controller components declare what data they need seems a sensible one.

briandipalma commented 9 years ago

This sort of a system could be provided by using Baobab btw.

nhunzaker commented 9 years ago

Yes! This has definitely been on my mind. My answer at first was just to use smart shouldComponentUpdate methods with immutable data structures, however that's not satisfactory for every use case.

It's definitely a concern, I just haven't figured out the right way to handle it yet. I really need to dig deeper into NuclearJS. We share a lot of similar ideas and I have a lot of respect for it.

nhunzaker commented 9 years ago

Okay. I like getDatabindings quite a bit. We don't have any answer for Nuclear's getters, but I've definitely played around with the idea of passing a "change set" with every event emission. This would look something roughly like:

app.listen(function(state, changeset) {
    // changeset = { user: { id_hash : { name: 'Fiz' }
})

You could then operate upon that change set to make a decision about how to update state.

However this requires pretty intimate knowledge of the underlying data layer. We could do this quite easily with Foliage, which Microcosm uses as a data layer out of the box. However we'd need to handle non-traditional data libraries too. This is why I've more or less punted on the idea. Additionally, you basically end up doing the same comparison as shouldComponentUpdate.

Back to "getters". This is a very cool idea. If we added a DSL for that, only updating when the computed values change, would that be sufficient?

I think either way, this would be a pretty cool addition, one that we could layer on without too many ramifications.

What is the most attractive option for you?

briandipalma commented 9 years ago

I'm not currently using microcosm I'm just evaluating what I believe are the best libraries of the new crop of Flux implementations. I've nuclear-js, redux, microcosm and cerebral in that list but I haven't yet made a decision as to which is best. I think part of the problem is that I haven't fully specified the features I'd want from the best of breed modern flux implementation. So maybe I'm opening this issue prematurely.

I think what would be most helpful to each of the maintainers of these implementations is a clear specification and reasoning for the choices in the specification. I might just go a write up something and get back to you once I've done that.

For what it's worth I would recommend against the changeset idea (ugly code to check if the data you are interested in has changed).

Back to "getters". This is a very cool idea. If we added a DSL for that, only updating when the computed values change, would that be sufficient?

I'm not sure what you mean by a DSL but the getters approach is what I'd prefer from a flux implementation. The view controller explicitly listing it's data dependencies is a nice idea and resonates with how GraphQL/Relay works.

briandipalma commented 9 years ago

I don't understand why Foliage exists, is there a problem with Baobab?

nhunzaker commented 9 years ago

Thank you for your thoughts and I'm thrilled that Microcosm has made your short list.

DSL was a poor (and inaccurate) choice of words. "Plumbing", or "mimicking NuclearJS" might have been more accurate. I am particularly excited to dig into this, it's one of the final problems we haven't really stated an opinion on.

What I'm about to say is something I should probably really document better. I'll make a note to do it after this comment.

Microcosm's original goal was to provide state isolated flux in a way that was easily embeddable in libraries. I found myself wanting a flux on complicated React component libraries (like photo galleries or rich text editors), and found the framework space lacking. I did not anticipate that the concepts within Microcosm (stateless stores, centralized application data) would grow to be very attractive for larger applications.

This brings me into one of the opinions of Microcosm, which is to keep it small and extendable. This is to mitigate conceptual overhead, "framework expertise", and (less importantly) bloat as much as we can. I want to lean on JavaScript as much as I can so that; to enforce patterns in Microcosm instead of framework specific specific code. One of the great things about Microcosm right now is that you can completely remove it and you are still left with the majority of your code as pure JavaScript.

This has a tremendous benefit benefit for embeddability, but it runs against feature parity with other frameworks and out-of-the-box support for larger apps. It's definitely on our radar, particularly after witnessing the success of the Microcosm's concepts on small to medium sized apps (and greater acceptance in the community as "good ideas".

That sort of brings me into Foliage, which is currently the "data layer" for Microcosm. I created Foliage because I found myself wanting an extremely lightweight immutable data store that behaved loosely similarly to a cursor library such as Baobab. It has been very useful in the libraries we've built it with.

This is not to say anything against Baobab. I think it is a fantastic library; the first I would consider if taking a cursors approach. I just couldn't justify throwing into a react component library.

As we generalize Microcosm for larger, stand-alone applications, Foliage's utility quickly diminishes. ImmutableJS pretty much replaces all use cases for Foliage on medium+ projects. I'd like to decouple Foliage from Microcosm in a future release.

Sort of as a final note, specifying data needs, similarly to GraphQL or otherwise, is not a problem we have attempted to solve yet, although it is being discussed at Viget quite a bit as the desire to use Microcosm on larger projects is becoming more frequent.

That was a longer comment than I anticipated, however I love to talk about this stuff. If you are willing to share, I would greatly appreciate learning more about the needs you have for your particular project. Having more information about how to accommodate real world demands on larger applications (outside of our own) is important for making the best Flux implementation we can.

briandipalma commented 9 years ago

I figured you didn't mean DSL. Now I understand.

Interesting that you were looking for a flux implementation that would work for complex components, I blogged a bit about the problems (old blog so of low value now) with flux in such a context. It looks to me like many others came across the same issues and took similar paths to solve them.

I don't know if using a single Microcosm for large applications is an approach I'd take, I can envision clashes between instances of the same component when triggering actions.

I like the fact that Microcosm doesn't expect you to write code that is coupled to the framework.

Sadly I won't be able to use any of these flux implementations for my current project as it's already shipping and there is no interest in creating new components or in rewriting old ones. I just wanted to make sure I understood the pros and cons of these flux implementations. Hence my comment about a specification.

When I get enough flesh on it so that it might explain the use cases I have in mind for complex applications I'll create a new issue in the repo to see what your thoughts are on it. Until then I'll close this issue as it's not fully fleshed out yet.