goatslacker / alt

Isomorphic flux implementation
http://alt.js.org/
3.45k stars 323 forks source link

Alt data dependency, how to handle elegantly and idiomatically #621

Open rmoskal opened 8 years ago

rmoskal commented 8 years ago

More of a question than a bug report...

I'm using alt as my flux implementation for a project and am having trouble wrapping my head around the best way to handle loading stores for two related entities. @dougajmcdonald posed a similar question here, but it didn't get any play

http://stackoverflow.com/questions/33253441/alt-data-dependency-between-actions-not-stores

I have two entities:

enter image description here

They are related one to one. I have many jobs. I use a source to load the job store:

module.exports = {
    fetchJobs() {
        return {
            remote() {
                return axios.get('api/platform/jobs');

            },....

When the job factory is loaded with data I would like to fill the conversation store with the associated conversations, also fetched from a remote api. Seems like a job for something like waitFor(), but waitFor() won't help me since it's an action that depends on the store data being available.

My naive solution is to reference the conversation actions from the job store and to kick off an event once it gets it's data. Something like this:

var jobActions = require('../actions/Jobs');
var conversationActions = require('../actions/Conversations');

class JobStore {
    constructor() {
        this.bindListeners({
            handlefullUpdate: actions.success
        });...

    }

    handlefullUpdate(jobs) {
    this.jobs = jobs;
    conversationActions.fetch.defer(jobs);
}

}

Calling conversationActions.defer.fetch() seems wrong as it violates the dictum that stores shouldn't generate events. The point is brought home by being forced to used defer to dispatch an action in the middle of a dispatch.

Worse still, my job store has to hold a reference to any dependent entities so it can dispatch the appropriate action. Here I have one, but I could imagine many. In terms of the dependencies between entities this seems totally backwards.

A couple of alternatives come to mind:

I can call the api/platform/jobs endpoint in the source/action where I fetch all conversations, just to get the id. That seems wasteful. The original approach is more efficient. This seems truer to the spirit of flux.

I could also have single action/source that fetches both, returning {jobs:{}, conversations: in the action} and use it populate both stores. But this approach seems unnecessarily complicated to me.

But am I missing another way? It seems strange that such a common use case would break the "injunction" against dispatching events from you flux stores and/or force me to jump through so many hoops.

if anyone wants to rack up some SO points, I raised the question there too: http://stackoverflow.com/questions/35344635/alt-data-dependency-how-to-handle-elegantly-and-idiomatically

rmoskal commented 8 years ago

Guess no one else is puzzled by this. I wound up separating each of the entities into their own action/store/source, with no cross-talk. I make an addition ajax call in the JobConversation source to first fetch the job and the conversation id.

At the moment I think the clarity is worth the extra back end call. I also realize how I would like it to work. I'd like to be able to specify the dependency in the exportAsync() method on a store. I'd love to be able to say:

class JobConvesationStore {
    constructor() {
        this.exportAsync(source, JobStore);
    }

The async method JobConversationStore would not be called until the JobStore had its data. The intent is easy to read and no complex action choreography would be needed.