Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

store.dispatch(...).then is not a function #1251

Closed
dagatsoin opened this issue Jan 19, 2016 · 11 comments
Closed

store.dispatch(...).then is not a function #1251

dagatsoin opened this issue Jan 19, 2016 · 11 comments

Comments

@dagatsoin
Copy link

Hi,

I am learning advanced redux part of the doc. I use Meteor.
I have a error at the Async part store.dispatch(...).then is not a function

Here is my entry point code:

import thunkMiddleware  from 'redux-thunk'
import createLogger  from 'redux-logger'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import { TodoApp } from './components/TodoApp.jsx'
import { rootReducer } from './reducers.js'
import { fetchObjects } from './map/actions'

if (Meteor.isClient) {
  Meteor.startup(function () {

    const loggerMiddleware = createLogger();

    const createStoreWithMiddleware = applyMiddleware(
      thunkMiddleware, // lets us dispatch() functions
      loggerMiddleware // neat middleware that logs actions
    )(createStore);

    const store = createStoreWithMiddleware(rootReducer);

    store.dispatch(fetchObjects()).then(() =>
      console.log(store.getState())
    );

and my fetchObjects action:

export const fetchObjects = () => {
  return dispatch => {
    dispatch(requestMapObjectsAction());

    return setTimeout(()=>{
      dispatch({
        type: RECEIVE_MAP_OBJECTS,
        objects: {'id':'56789', 'id':'567Zdz'},
        receivedAt: Date.now()
      })
    },2000);
  }

Could this be related to #1240 ? EDIT: No it does not.

@dagatsoin
Copy link
Author

I also tested with the action of the async example and it works:

export const fetchObjects = (reddit = 'reactjs') => {
  return dispatch => {

    dispatch(requestMapObjectsAction(reddit))

    return fetch(`http://www.reddit.com/r/${reddit}.json`)
      .then(response => response.json())
      .then(json => dispatch(receiveMapObjectsAction(reddit, json)))
  }
}

So I don't really understand what is going on. Could someone explain me what are the requirements for a return function in async action?

My final goal is to use a Meteor async method but I get the same error.

export const fetchObjects = () => {
  return dispatch => {
    dispatch(requestMapObjectsAction());
    return Meteor.call('getMapObjects', (error, json) => {
      dispatch(receiveMapObjectsAction(json));

    });
  }
}

@gaearon
Copy link
Contributor

gaearon commented Jan 19, 2016

Dispatch will just return whatever you return from that function. If you return fetch(...).then(...) you are returning a Promise. However setTimeout() does not return a Promise. Therefore what you get from dispatch() in this case is the timeout ID.

If you'd like, you can always return Promises for consistency. Redux doesn't enforce that, it is completely up to you what to return from thunks. There is no way Redux could've figured out you are waiting for an interval unless you explicitly create a Promise for it and return it.

const delay = (ms) => new Promise(resolve =>
  setTimeout(resolve, ms)
);

export const fetchObjects = () => {
  return dispatch => {
    dispatch(requestMapObjectsAction());

    return delay(2000).then(() => {
      dispatch({
        type: RECEIVE_MAP_OBJECTS,
        objects: {'id':'56789', 'id':'567Zdz'},
        receivedAt: Date.now()
      })
    });
  }
}

@yasir-netlinks
Copy link

I am having problem using redux-saga, I am trying to navigation from a child component:

import configureStore from '../../../configureStore';

const store = configureStore();
class SomeComponent extends Component{
    //...
    goToProfile = () => {
        store.dispatch(NavigationActions.navigate({ routeName: 'Profile' }))
    }
}

I get this error: Cannot read property 'default' of undefined and sometimes this error: undefined is not an object(evaluating '_reducers2.default')

can someone help, thanx

@markerikson
Copy link
Contributor

@yasir-netlinks :
This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux where there are a lot more people ready to help you out - you'll probably get a better answer faster. Thanks!

@Hiroki111
Copy link

Hiroki111 commented Nov 26, 2017

I'm not challenging you, @dagatsoin, but I suppose store.dispatch(...).then may cause a bit of confusion for your colleagues, provided that you're writing code in a team environment.

According to Redux' doc, the data flow is meant to be a strict unidirectional data flow.

dataflow

If you put then just after dispatch(action), the action will affect not only a reducer but also the component in which you put the dispatch(action). To me, it looks like a bidirectional data flow.

@markerikson
Copy link
Contributor

@Hiroki1116 : You can only do dispatch(something).then() if a middleware is returning a promise. For example, redux-thunk returns whatever your thunk function returns, allowing you to do this:

function fetchData() {
    return (dispatch) => {
        return fetch("/someData")
                      .then(response => response.json())
                      .then(data => dispatch(loadData(data))
    }
};

// elsewhere
dispatch(fetchData())
    .then() => {
        // do something now that the async call has resolved
    });

This can be a useful capability, and is not going against Redux's unidirectional data flow.

For more info, see the articles in the Redux Side Effects section of my links list.

@Hiroki111
Copy link

@markerikson

Thanks for your info.

Of course, dispatch(something).then() itself isn't bi-directional.
It depends on the implementation whether it's uni or bi-directional.

For example...

        dispatch(action(values)).then((result) => {
		//do something
	}).catch((error) => {
		throw new SubmissionError({_error:  error });
	});		

If dispatch(action(values)) leads to an error, new SubmissionError({_error: error }) will be thrown, which will directly change the UI without changing reducer and Redux' state.
(By the way, this SubmissionError is from Redux-Form)
Likewise, the //do something part shouldn't change the UI either for the same reason.

As long as then (and catch) won't update UI directly, the data flow is seen as unidirectional.

Correct me if I'm wrong!

@markerikson
Copy link
Contributor

@Hiroki1116 : Two thoughts.

First, you're worrying about the data flow aspect way too much. The important aspect is dispatched action -> reducer -> subscribers -> UI updates. Async stuff happens separate from that (or, alternately, can be what triggers the dispatched action in the first place).

Second, in that particular snippet, throw new SubmissionError is just going to reject the promise it's in, and not affect the UI at all.

But seriously, don't worry about it :)

@dagatsoin
Copy link
Author

dagatsoin commented Nov 26, 2017

@Hiroki1116 I understand your concerns and I agree with @markerikson

I had the dispatch().then().catch() interrogation few weeks ago with Vuex in a team environment but it is also true with Redux.

Un-intuitively, my conclusion is that the possibility of returning something from the dispatch enforces the separation of concern principle.
By using a return value (promise or anything), you don't overfill the global store with data which concern only a specific component. For exemple a UserFormCreation, a bit like in @markerikson exemple:

  • you want to tell the App that an API call is pending (global concern)
  • but the User object is just the concern of the current main component (UserFormCreation).

Still, the data flow is respected.

In addition I would argue that the respect of the unidirectional data flow is considered OUTSIDE
the view context:

  • Redux is meant to handle the data, not the view. By nature (without connect or anything) it works without a view.
  • From the point of view of the Flux architecture we don't care what the view does. We just inject the store, and wait for a dispatched action from it. What is inside the view is the developper-best-practices concern and are outside the scope of Flux.

PS: by the way I had not thanks @gaearon for his response! Thanks you :)

@Hiroki111
Copy link

Thank you for your thoughts, @markerikson & @dagatsoin !

@omarish

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants