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

Issue with async middleware + replaying #142

Closed
StevenLangbroek opened this issue Oct 6, 2015 · 3 comments
Closed

Issue with async middleware + replaying #142

StevenLangbroek opened this issue Oct 6, 2015 · 3 comments

Comments

@StevenLangbroek
Copy link

I'm having an issue with my "async" middleware. It follows the pattern of redux-promise-middleware in a way, but is tailored to our needs:

export default ({ getState, dispatch }) => (next) => (action) => {
  if(typeof action.types === 'undefined' || typeof action.url === 'undefined') return next(action);

  const [ requestType, successType, failureType ] = action.types;
  const { url, resource } = action;

  const getParams = resources[resource];

  const state = getState();

  const params = getParams ? getParams(state) : { shouldFetch: true };

  if(!params.shouldFetch) return next(action);

  const ENGINE_URL = state.metadata.env === 'development' ? 'http://server.com' : '/proxy';

  next({
    type: requestType,
    $isLoading: true
  });

  return fetch(`${ENGINE_URL}${url}?${stringify(omit(params, 'shouldFetch'))}`)
    .then(status)
    .then(json)
    .then(data => next({
      type: successType,
      data
    }), error => next({
      type: failureType,
      error
    }));
}

My ActionCreator then looks like this:

  return {
    type: GET_OFFERS,
    types: [REQUEST_OFFERS, RECEIVE_OFFERS, FAILURE_OFFERS],
    method: 'get',
    resource: 'offers',
    url: '/offer'
  }

Note: the GET_OFFERS type is only there because a type is required on an Action.

The problem comes when I start replaying actions. As soon as I move past the "original" action when moving back, it will duplicate actions. So my app needs 3 actions to start (SET_METADATA, REQUEST_OFFERS and RECEIVE_OFFERS), but as soon as I move back along REQUEST_OFFERS it fires the async actions again. Am I doing something wrong here?

Edit: So I found the underlying issue: when my (connected) root component receives props, if there's metadata (_.size(metadata)), I call the requestOffers action creator. This is intended behavior for my app, but will in the future make it hard to replay state from external sessions. Does anybody have any recommendations how to work around this?

Here's the code from my component:

  componentWillReceiveProps({ metadata, offerState }){
    if(size(metadata) && !offerState.$didLoad && !offerState.$isLoading){
      this.props.dispatch(requestOffers());
    }
  }

Ok, so I fixed this by moving the SET_METADATA up a level, and only mounting the component which initiates the request if there's metadata. Are there any recommended patterns for this sort of behavior yet? This took me by surprise a bit (Yay I have time travelling! Wait, what?), I wouldn't mind writing some addition to the documentation about this.

@gaearon
Copy link
Contributor

gaearon commented Oct 7, 2015

Note: the GET_OFFERS type is only there because a type is required on an Action.

This is a misconception. It is only required by the time an action reaches the reducer. If your middleware interprets the action and instead dispatches other actions, it is only essential that they have types by the time the last middleware in chain dispatches them.

@gaearon
Copy link
Contributor

gaearon commented Oct 7, 2015

Can you provide a minimal example demonstrating the issue?
Maybe you can reproduce it on real-world example in Redux which now uses DevTools and write the steps?

@gaearon
Copy link
Contributor

gaearon commented Oct 17, 2015

Closing for inactivity.

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

2 participants