cyclejs-community/redux-cycles

Action from an http requetis fired twice

dbertella opened this issue · 3 comments

I have a component that at componentDidMount will fire an action that makes an http request.
What I want is to set an isFetching flag in the reducer.
Everything works as expected the first time I load the component, but the second the flag isFetching is never set to true.
I played just a little with the middleware during this weekend so I'm not really sure why I have this problem.

This is my main, I just readapted the code from one of the example.

const fetchPostById = (sources) => {
  const post$ = sources.ACTION
    .filter(action => action.type === ActionTypes.POST_REQUESTED)
    .map(action => action.postId);

  const request$ = post$
    .map(postId => ({
      url: `${BASE_URL}posts/${postId}`,
      category: 'post'
    }));

  const response$ = sources.HTTP
    .select('post')
    .flatten();

  const action$ = xs.combine(post$, response$)
    .map(arr => actions.receivePostById(arr[0], arr[1].body));

  return {
    ACTION: action$,
    HTTP: request$
  }
}

And this is the reducer:

const post = (state = initialPost, action) => {
  switch (action.type) {
    case 'POST_REQUESTED':
      return {
        ...state,
        isFetching: true,
      };
    case 'POST_RECEIVED':
      return {
        ...state,
        isFetching: false,
        [action.postId]: action.post,
      };
    default:
      return state;
  }
};

If I log what's going on with my request, I can see that isFetching is false at the beginning, then the action POST_REQUESTED is fired and isFetching is set to true. When POST_RECEIVED is being fired (from the cycle code) isFetching goes back to false. That's the normal case.
On the next time POST_RECEIVED is being fired two times for the same action and isFetching flag is never set to true.

screen shot 2017-02-27 at 18 19 34

You can see an online version here https://dbertella.github.io/food-and-quote/ in case.
Am I missing something?

The problem is with the use of combine: https://github.com/staltz/xstream#-combinestream1-stream2

The second time you call the POST_REQUESTED action from react, it's gonna emit POST_RECEIVED even without a response (because combine uses the latest response).

I think you may need sampleCombine instead (https://github.com/staltz/xstream/blob/master/EXTRA_DOCS.md#-samplecombinestreams) But make sure the source stream is the response.

Hope it makes sense. Let me know if you can figure it out.

Thank you very much! I'll look into it and let you know!
Edit
I couldn't find the right way to make sampleCombine working instead, but I found out that in my case I don't need the combine at all, at least not for the single post.

const action$ = sources.HTTP
    .select('post')
    .flatten()
    .map(res => actions.receivePostById(res.body.id, res.body));

That makes the trick, but I will have to understand better cycle for more advanced stuff anyway.
Thank you again!

Yeah if you don't need the post info in the stream, then you don't need combine 😃

Anyway in case you need the post this should work:

import sampleCombine from 'xstream/extra/sampleCombine'

const action$ = response$
  .compose(sampleCombine(post$))
  .map(([ response, post ]) => actions.receivePostById(post, response.body))