0

I am new to react and redux and I am facing a very strange issue. I have an action as follows:

export function getStorySnippetsAction(snippetsLink, channel) {

return dispatch => {
    let storiesSnoppet = [];

    for (var i = 0; i < snippetsLink.length; i++) {

        $.ajax({
            url: snippetsLink[i],
            dataType: 'json',
            cache: false,
            success: function (data) {
                storiesSnoppet.push(data);
                console.log("xxxxxxxxxxxxxxxxxxxxx");
                console.log(storiesSnoppet);

            }.bind(this)
        });
    }


    dispatch({
        type: "SET_STORY_SNIPPET",
        payload: {"channel": channel, "storiesSnippet": storiesSnoppet}

    });

};

}

As you can see I have some ajax calls in a loop and then I use dispach to set state. Now my problem is ajax calls are asynchronous so when ajax sends request the dispach happens and does not wait for all ajax call to finish. Also the following is what I get when I print out the payload.action in console:

console log

As you can see when the object is collapsed it has a size of 0 and when I expand it I can see some objects in an arraylist. My only guess for such a strange behavior is that the asynch call complete after the rendering and since componentdidmount( which I call getStorySnippetsAction in it) happens just once the rerender does not happen again and I do not see any result. Someone told be to put my call to getStorySnippetsAction inside componentdidupdate but when I do that then it seems that an infinite loop happens and the page never loads ( I can see that in the console the same thing is written again and gain which means that component did update invokes infinitely). Now I am totally stuck. If I call getStorySnippetsAction in componentdDidUpdate I will be in an infinite loop and if I call it in componentDidMount ajax takes longer that rendering and rendering happens with empty array instead of loading the ajax result.

Can anyone help? (Even an idea may help me to fix this issue. Thanks a million)

Hamed Minaee
  • 2,480
  • 4
  • 35
  • 63
  • Hey, I'd recommend you to take a look at redux-thunk. https://github.com/gaearon/redux-thunk – Martin Mihaylov Feb 15 '17 at 20:51
  • @MartinMihaylov I think OP is using thunk already.. otherwise they wouldn't be returning a function with `dispatch` – azium Feb 15 '17 at 20:53
  • Alright, let me try to understand your problem. From your code, it looks like initially storesSnippet is set to an empty array, so nothing will appear on your screen. The reason here is that like you stated, the ajax call is async. So, what you would like to do is to call dispatch following the completion of the ajax call, correct? – Cheng Sieu Ly Feb 15 '17 at 20:55
  • @ChengSieuLy Thanks for answering my answer would be yes but a better approach would be to rerender the page when ajax calls are finished – Hamed Minaee Feb 15 '17 at 20:57
  • Put your `dispatch` call inside your success function.. though since its multiple calls you'll probably want to use something like Promise.all. you need to read more about how to manage async calls. http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call/14220323#14220323 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – azium Feb 15 '17 at 20:57
  • @MartinMihaylov Thanks but I already use thunk and I have store and which amnages any change to state. the problem is that state after ajax calls state does not update anymore so no change is happening – Hamed Minaee Feb 15 '17 at 20:59

2 Answers2

1

Why dont you put your dispatch after a callback from async module instead of for loop?

Example:

import parallel from 'async/parallel'
export function getStorySnippetsAction(snippetsLink, channel) {

return dispatch => {
    let storiesSnoppet = [];
    const ajaxFunctionArray = snippetsLink.map(i => {
      return function(callback) {
        $.ajax({
            url: snippetsLink[i],
            dataType: 'json',
            cache: false,
            success: function (data) {
                storiesSnoppet.push(data);
                callback(null)
                console.log("xxxxxxxxxxxxxxxxxxxxx");
                console.log(storiesSnoppet);
            }.bind(this)
        });
      })
    //it ill make the posts in parallel and them dispatch just after all have finished

    async.parallel(ajaxFunctionArray, function (err, result) {
      dispatch({
        type: "SET_STORY_SNIPPET",
        payload: {"channel": channel, "storiesSnippet": storiesSnoppet}

    });   
    }

};
Robsonsjre
  • 1,586
  • 11
  • 17
1

The idea here is that you turn each request into a Promise and store it in an array. Once each request finishes, we will resolve it with the data. Finally, once all the promises in the array are resolved, then we call dispatch with the resolved values.

export function getStorySnippetsAction(snippetsLink, channel) {

  return dispatch => {
    let storiesSnoppet = [];

    for (var i = 0; i < snippetsLink.length; i++) {
      storiesSnoppet.push(new Promise((resolve, reject) => {
        $.ajax({
          url: snippetsLink[i],
          dataType: 'json',
          cache: false,
          success: function(data) {
            resolve(data);
            console.log("xxxxxxxxxxxxxxxxxxxxx");
            console.log(storiesSnoppet);
          }.bind(this)
        });
      }));
    }

    Promise.all(storiesSnoppet).then((values) => {
      dispatch({
        type: "SET_STORY_SNIPPET",
        payload: {
          "channel": channel,
          "storiesSnippet": values
        }
      });
    });
  };
}
Cheng Sieu Ly
  • 1,106
  • 9
  • 7
  • Thanks Cheng worked. Just to check my approach do you think it is a good idea to use promise.all? is there anyway to handle this without using promise all to take advantage of asynchronous and partial loading? ( I appreciate any suggestion since I want to take a right step) – Hamed Minaee Feb 15 '17 at 21:32
  • You're right. When you have to wait for potentially thousands of request to finish, that could be a problem :) So speaking of loading as you go, a possible idea is that as a request finishes, you dispatch a new action with the new added snippet to your array of snippets (taken from the store). The dispatch would be inside the success function and there will be no need for promises anymore. Also, you can cap the number of request so basically request only what you really need. These are some suggestions but I'm sure there are libraries out there to help? I can't think of any on top of my head. – Cheng Sieu Ly Feb 15 '17 at 21:57
  • I think I would just cap the number of request and request what is needed then make more request if needed. – Cheng Sieu Ly Feb 15 '17 at 22:01