jeffbski / redux-logic

Redux middleware for organizing all your business logic. Intercept actions and perform async processing.
MIT License
1.81k stars 107 forks source link

Getting dispatch to work on recursive calls. #53

Open xmaster484 opened 7 years ago

xmaster484 commented 7 years ago

Not sure if this is the correct place for this. I am currently using redux logic and i'm trying to deal with a paginated API. I am trying to pass dispatch to an auxiliary function that will attempt to pull all the pages of the resultset and concat them to the store. I pass the dispatch to this auxiliary function and it works fine, but when i recurse on this same auxiliary function, to try to pull the next set of results and pass the dispatch in again, it doesn't actually dispatch any actions, and it doesn't throw any errors. I have made sure multiple dispatches were turned on by dispatching the same action 3 times before the recursion started and that worked.

Any ideas why the dispatch wont work past the first auxiliary function call?

ksloan commented 7 years ago

You should post example code. It's hard to understand what you mean by 'pass the dispatch'?

Are you saying that you have a recursive function within process() that is dispatching multiple actions? An error might be occurring after the first dispatch that essentially stops subsequent actions from being dispatched properly. Might be related to #50.

xmaster484 commented 7 years ago

@ksloan const getDataUntilDone = (data, dispatch) => { const links = data.links.next; dispatch(CONCAT_DATA(data.data)); if (links !== null) { axios.get(links).then(resp => { getDataUntilDone(resp.data, dispatch); }); } };

export const dataLogic = createLogic({ type: SOME_TYPE, process({ getState, action }, dispatch, done) { axios.get('some-api-url') .then(resp => getDataUntilDone(resp.data, dispatch)) .catch((err) => { console.error(err); }) .then(() => done()); }, });

Basically the first dispatch call in getDataUntilDone works, and even if i were to copy the dispatch call a few more times, they would all dispatch fine.

The problem is that it wont dispatch anymore after it starts recursing. before recursion starts its fine, after it starts, it stops working.

CONCAT_DATA is the action that concats the API resultset to the store, I know it works fine with the reducer and everything (manual testing).

Any ideas?

Edit: Ill also like to add that the logic for getting all the pages and existing works fine, I have outputted all the the results i have received and they were all there, literally only the dispatch isnt going through.

ksloan commented 7 years ago

My guess is that done() is being called after the first getDataUntilDone() invocation, but before the second. Do some logging to make sure that's not the case, and try returning the promise from getDataUntilDone(). ex... return axios.get(links)...

xmaster484 commented 7 years ago

@ksloan seems like you were right, the done() was being executed before the other dispatches, but adding the return just made it dispatch twice (instead of once when there was no return) and then return, instead of dispatching all 7 actions that it should have been.

Edit: I feel like there should be a better way to ingest paginated API results, are you familiar with any other way? This method seems a bit quick and dirty.

erkiesken commented 7 years ago

I would solve it with some RxJS, like so:

import { Subject } from "rxjs/Subject";
import "rxjs/add/operator/finally";

export const dataFetchLogic = createLogic({
    type: FETCH_PAGINATED,
    process({ action, axios }, dispatch, done) {
        const startUrl = action.payload.url;
        // Lets use a stream to handle data, errors and final completion
        const data$ = new Subject();

        // This recursively fetches and puts data into data$
        const fetchData = (url) => {
            axios
                .get(url)
                .then((res) => {
                    data$.next(res.data);
                    const nextUrl = res.links.next;
                    if (nextUrl) {
                        // Get next chunk of data
                        fetchData(nextUrl);
                    } else {
                        // No more chunks to get
                        data$.complete();
                    }
                })
                .catch((err) => data$.error(err));
        }

        data$
            // This makes sure logic is done when data$ ends with complete or some error
            .finally(done)
            .subscribe(
                // Next data chunk handler, saves via another action
                (data) => {
                    dispatch(SAVE_AND_CONCAT(data));
                },
                // Error handler, if any recursive fetching fails
                (err) => {
                    console.log(err);
                }
            );

        fetchData(startUrl);
    }
})