jesseskinner / hover

A very lightweight data store with action reducers and state change listeners.
MIT License
98 stars 7 forks source link

Single getter paradigm seems too constraining #5

Closed winkler1 closed 9 years ago

winkler1 commented 9 years ago

Sniffing around at flux implementations here. Hoverboard's ultra minimalist philosophy seems appealing, but onViewById/updateItem example seems like a not-great design. Would work in some cases but very confining.

Also, json serialize/deserialize seems a high to provide a read-only copy of the whole state. Could be avoided by letting users expose query functions. Advantages:

jesseskinner commented 9 years ago

First, big thanks for taking the time to review Hoverboard and share your thoughts.

Regarding the JSON serialize/deserialize - I agree completely. The latest version 1.3.0 no longer does this. (See #2). Now state is mutable by default, and users can implement whatever mutation protection they prefer, if any. So it's easy to use serialization or Object.assign or something else. I haven't tested out using an immutability library yet but that should work as well.

About the larger concern you have with a single getter, I gave this some thought early on. I'd originally planned to allow defining any number of get* methods just like on* methods. I went with a single getter for a few reasons. I liked the simplicity, as you noted. I also felt that exposing methods with arguments and return values made it too easy to break out of the Flux single flow of data.

In some sense, I feel that even an ID being passed through to getById is data, it's coming from a user action of some sort. So it should go through the same dispatcher flow via an action. This allows something like an ItemView page to be agnostic about what the parameters are that fetched the item. Instead, the controller can pass the parameters (from the URL or whatever) on to the stores as data, and then get the state back from the stores as they respond to those parameters, and pass the results on to the view.

Ultimately, what I'm trying to achieve with the single getter is a simple API for pulling state out of the stores and passing it on to a view. I envisioned perhaps a store-per-view kind of setup, where the bottom-most store does a kind of map/reduce with other stores, generating the final object that can be passed on to the view as props, like so:

UserProfileStore.getState(function(userProfileState) {
   var el = React.createElement(UserProfile, userProfileState);
   React.render(el, document.body);
});

With the recent changes, you are now allowed to have functions in your state, so I suppose your state could be a model with getter functions on it. Perhaps that allows the flexibility you felt was lacking?

Looking forward to your thoughts.

jesseskinner commented 9 years ago

Further to the last point, here's an example that is now possible with recent changes. You can now maintain a private state and expose multiple getters with an API via a custom getState method. It should allow for much more flexibility in how stores can expose and manage their state.

var store = Hoverboard({
    getState: function(state) {
        // return public state API with multiple getters
        return {
            getAll: function(){
                return state.list;
            },
            getById: function(id){
                return _.find(state.list, {id:id});
            }
        };
    },
    onList: function(list) {
        // notify state listeners something has changed
        this.setState({ list: list });
    }
});

store.list([ { id: 123, some: 'thing' } ]);

// can access the getter this way
var item = store.getState().getById(123);

// or this way
store.getState(function(state){
    var item = state.getById(123);
});

Thoughts?

winkler1 commented 9 years ago

Starring this email, will think through in the morning when have more brain power. Will also try to use it for real.. think I have a new project that'll work for it.

jesseskinner commented 9 years ago

That's awesome, looking forward to it.

winkler1 commented 9 years ago

Interesting talk - https://www.youtube.com/watch?feature=player_embedded&v=xKlBivF4f44#t=3414..

jesseskinner commented 9 years ago

Interesting indeed, got me thinking about a few things.

At times I wonder if Hoverboard's merger of actions, dispatcher and stores could lead to problems, but I feel like that's a trade off that allows you to be more explicit about calling actions on particular stores and listening to store state directly instead of coordinating store responses via waitFor.

I originally wanted to simplify the assembly of async state and management of actions for my React components, and to aim for all the data entering my app as props, coming via some kind of state listener callback system. I decided I did want to use the Flux approach for this, but after reviewing a dozen implementations and being unsatisfied, I decided to write Hoverboard to achieve the single flow of data and state listeners but with a simpler API and approach.

Anyway, thanks for the link, I'm enjoying thinking this stuff through and holding Hoverboard up against any discussion of Flux to see where and how it fits in.