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

Can not make 'latest' property work #153

Closed samuelsd1 closed 5 years ago

samuelsd1 commented 5 years ago

Hello, I'm currently trying trying to test the 'latest' property on a logic, and It seems to me that either I have a mistake somewhere, or it really does not work.

Here is the code example:

import { createFeature } from 'feature-u';
import { slicedReducer } from 'feature-redux';
import { createLogic } from 'redux-logic';

const featureName = 'settings';

const myReducer = (state = null, action) => {
    console.log('settings reducer', action);
    return state;
}

const myLogic = [
    createLogic({
        type: '*',

        process({ getState, action }, dispatch, done) {
            //console.log('process', action);
            //console.log('done with', action);
            done();
        }
    }),

    createLogic({
        type: 'myAction',
        cancelType: 'myAction_cancel',
        latest: true,
        process({ getState, action }, dispatch, done) {
            new Promise((resolve, reject) => {
                console.log('LATEST process', action);
                setTimeout(() => {
                    console.log('LATEST done', action);
                    resolve();
                }, 1000);
            })
                .then(() => done())
                .catch((err) => console.log('erred'));
        }
    })
];

export default createFeature({
    name: featureName,
    reducer: slicedReducer(featureName, myReducer),
    logic: myLogic,
    appWillStart({ fassets, curRootAppElm }) {
        console.log('settings appWillStart');
    },
    appInit({ showStatus, fassets, appState, dispatch }) {
        console.log('settings appinit');
    },
    appDidStart({ fasstes, appState, dispatch }) {
        console.log('settings appDidStart');
        for (let i = 0; i < 10; ++i) {
            if (i > 3) {
                dispatch({ type: 'myAction_cancel' });
            }
            dispatch({ type: 'myAction', payload: `${i}` });
        }
    },

});

As you can see, I'm dispatching multiple 'myAction' actions, and even dispatching later cancel actions, and for some reason - I still see the prints of 'LATEST done with the action' for each action, although there were actions that were dispatched after it (and cancellation actions dispatched too):

settings reducer {type: "myAction", payload: "0"}
settings reducer {type: "myAction", payload: "1"}
settings reducer {type: "myAction", payload: "2"}
settings reducer {type: "myAction", payload: "3"}
settings reducer {type: "myAction_cancel"}
settings reducer {type: "myAction", payload: "4"}
settings reducer {type: "myAction_cancel"}
settings reducer {type: "myAction", payload: "5"}
settings reducer {type: "myAction_cancel"}
settings reducer {type: "myAction", payload: "6"}
settings reducer {type: "myAction_cancel"}
settings reducer {type: "myAction", payload: "7"}
settings reducer {type: "myAction_cancel"}
settings reducer {type: "myAction", payload: "8"}
settings reducer {type: "myAction_cancel"}
settings reducer {type: "myAction", payload: "9"}
LATEST process {type: "myAction", payload: "0"}
LATEST process {type: "myAction", payload: "1"}
LATEST process {type: "myAction", payload: "2"}
LATEST process {type: "myAction", payload: "3"}
LATEST process {type: "myAction", payload: "4"}
LATEST process {type: "myAction", payload: "5"}
LATEST process {type: "myAction", payload: "6"}
LATEST process {type: "myAction", payload: "7"}
LATEST process {type: "myAction", payload: "8"}
LATEST process {type: "myAction", payload: "9"}
LATEST done {type: "myAction", payload: "0"}
LATEST done {type: "myAction", payload: "1"}
LATEST done {type: "myAction", payload: "2"}
LATEST done {type: "myAction", payload: "3"}
LATEST done {type: "myAction", payload: "4"}
LATEST done {type: "myAction", payload: "5"}
LATEST done {type: "myAction", payload: "6"}
LATEST done {type: "myAction", payload: "7"}
LATEST done {type: "myAction", payload: "8"}
LATEST done {type: "myAction", payload: "9"}

Am I getting this wrong?

With much thanks, David

jeffbski-rga commented 5 years ago

David,

Thanks for writing. I think I can clear up the confusion. Basically what happens with a cancel or a latest is that any dispatches that would occur from work being done in the logic would be thrown out. So let's say in your setTimeout that you were then going to dispatch something, say you had fetched data and then would be dispatching a result action with the results. Then your view would update with the values. If the work had been cancelled (by cancel action) or due to a latest (newer request) then those result actions would not be sent. This would prevent your view from doing extra unnecessary work and even more importantly preventing it from updating with out of date data (in the case that a newer request was pending/running).

So the key here is we don't have a way of interrupting any JS you were executing in the logic, but the results of that work would not be dispatched. Actually there are some situations that are more interruptible, like if you were using an RxJS Observable to fetch the data then redux-logic can tell it to abandon the fetch immediately for a cancel or latest, but for most non-observable work it is not as easy to stop work in the middle. You could listen for the cancel$ observable to do that but for most things it is sufficient that we simply do not send the out of date actions.

Hopefully this explains why your console logs are still running. If you added a dispatch in your set timeout then you will see how things are not sent forward for a cancel/latest.

Jeff

samuelsd1 commented 5 years ago

@jeffbski Thank you for the response! Very clear explanation, now it makes sense :)