martyjs / marty

A Javascript library for state management in React applications
http://martyjs.org
MIT License
1.09k stars 76 forks source link

Fetch returning NotFoundError even when state is set #97

Closed FoxxMD closed 9 years ago

FoxxMD commented 9 years ago

First off, fantastic flux implementation!

I am trying to do a simple fetch using localStorage but my store always returns NotFoundError even though I can see the object set in the state (by calling getState or just checking this.state['myobj']. What am I doing wrong??

Store

var AuthStore = Marty.createStore({
    displayName:'Auth',
    handlers: {
        updateCredentials: AuthConstants.UPDATE_CREDENTIALS,
        clearCredentials: AuthConstants.CLEAR_CREDENTIALS
    },
    getInitialState: function() {
      return {};
    },
    getCredentials: function(){
        return this.fetch({
            id:'credentials',
            locally: function(){
                return this.state['credentials'];
            },
            remotely: function(){
                return credSource.getCreds();
            }
        })
    },
    updateCredentials: function(payload) {
        this.state['credentials'] = payload;
        credSource.setCreds(payload);
        this.hasChanged();
    },
    clearCredentials: function(payload) {
        this.state['credentials'] = undefined;
        credSource.setCreds(undefined);
        this.hasChanged();
    }
});
module.exports = AuthStore;

State Source

var credSource = Marty.createStateSource({
    type:'jsonStorage',
    storage: window.localStorage,
    getCreds: function(){
       return AuthSourceActionCreators.updateCredentials(this.get('credentials'));
    },
    setCreds: function(creds){
        this.set('credentials', creds);
    }
});
module.exports = credSource;

SourceActionCreator

var AuthSourceActionsCreators = Marty.createActionCreators({
    updateCredentials: AuthConstants.UPDATE_CREDENTIALS()
});
module.exports = AuthSourceActionsCreators;

Thanks for looking! I feel like I am overlooking something fairly simple but can't seem to find it...

jhollingworth commented 9 years ago

Hey, thanks for giving Marty a try :) Here's a working fiddle based on your example. Let me know if it makes sense or you've got questions

FoxxMD commented 9 years ago

Your example was perfect! I think my mistake was not assigning getCreds to the state in my store after retrieving it with remotely.

I have a conceptual question. You moved setting and clearing credentials in storage into the source action creator instead of in the auth store -- do you trying to restrict manipulating source data, whether http or storage, to actions in source action creator? From the examples it seemed like it was only used as a workaround for cyclic dependencies with require.

Basically what I'm seeing is

Stores

Action Creators

Source Action Creators

Is this a good way to look at logic? I'm trying to wrap my head around where to use what.

oliverwoodings commented 9 years ago

I think you are missing a few entities there:

Action

Stores

Sources

Views

Action Creators

Source Action Creators

FoxxMD commented 9 years ago

Thanks for the response @oliverwoodings, that's helping make me sense of things.

I'm still a little unclear on the proper flow of logic however, mostly on how to use action creators and source action creators.

Should Source Action Creators only be dispatched after the source has finished performing it's action, or can they be used to trigger source actions? In this same line, should stores be the only component interacting with sources? Or can action creators dispatch to source action creators (or interact with source directly)? I'm trying to find a line in the sand between what should interact with what, and in what order.

Here's a crude flow of logic and where I'm missing pieces in my understanding:

  1. Views
  2. Invoke action creator 2a. (should AC do anything else? or just)
  3. dispatch action on store
  4. update/get local state
  5. (if any) invoke source
  6. get/update source data
  7. invoke source action creator (Only needed if async?)
  8. dispatch action on store (again?? Only needed if async?)

Confusion in that flow also arises for sources like localStorage because in the fiddle example the store directly interacts with the source (setting local state from returned data) and the source does not invoke an action source creator. Is this because it's not asynchronous like a web request? Would I only use action source creators for aync? If not, what else would I use them for?

Thanks for the help :)

oliverwoodings commented 9 years ago

My understanding is that a Source Action should be dispatched when a source finishes doing something. @jhollingworth should be able to confirm this.

With non-async stuff you could cheat, but what if you wanted to swap that source to an async one in the future? It is best to treat all sources the same so that you don't have to refactor as much in the future.

FoxxMD commented 9 years ago

Since localStorage doesn't provide any promise based object (unlike http) how would you rewrite the fiddle example to use a source action creator? In my original post that's what I was struggling with actually, I was trying to use a source action creator to update the store hoping the local execution of getCredentials would see the changed state but it was not.

In the fiddle example

var AuthSourceActionsCreators = Marty.createActionCreators({
    updateCredentials: AuthConstants.UPDATE_CREDENTIALS(function (creds) {
        CredSource.setCreds(creds);

        this.dispatch(creds);
    }),
    clearCredentials: AuthConstants.CLEAR_CREDENTIALS(function () {
        CredSource.clearCreds();
    })
});

The credentials are set again (I guess not a big deal) before the action is dispatched and handled by the store. But the source doesn't call the source action, it just returns the data and the store sets the state.

jhollingworth commented 9 years ago

If you haven't read it yet, the Fetching data guide might be helpful.

Should Source Action Creators only be dispatched after the source has finished performing it's action, or can they be used to trigger source actions

Source Action Creators only exist to get around cyclic dependencies. They should only be invoked by State sources.

The previous fiddle was actually cheating with regards to getting the state from the Cred source. Any new state coming from a source should go through a source action creator so that all stores have the opportunity to do something with that state. This revised fiddle is how you should be doing it

FoxxMD commented 9 years ago

Thanks @jhollingworth . So I should implement my own promises when using local storage. :+1: Also it seems that the original problem I was having (that the action was failing even after state was set) was being caused by the Marty developer tools. I'll be making another issue for it shortly.

thanks so much for your help!

jhollingworth commented 9 years ago

no problem!