duy hau Han
duy hau Han

Reputation: 21

React component not receiving intermediate state when chaining actions in redux-saga

I have two actions TEST and TEST_DONE which both increment an id property in my redux state. I am using redux-saga to dispatch the second action TEST_DONE automatically whenever I dispatch the first action TEST from my component.

I expect the order of execution to go like this:

  1. component renders with initial value of testState.id = 0
  2. component dispatches TEST action
  3. component re-renders with testState.id = 1
  4. saga dispatches the TEST_DONE action
  5. component re-renders with testState.id = 2

Instead my component only re-renders when testState.id is updated to 2. I can't see the 1 value in the getSnapshotBeforeUpdate function. It shows 0 as the previous prop.

Why does the prop jump from 0 to 2 without receiving 1 in between?


saga.js:

export function* TestSagaFunc() {
    yield put({
        type: actions.TEST_DONE
    });
};

export default function* rootSaga() {
    yield all([
        yield takeEvery(actions.TEST, TestSagaFunc),
    ]);
};

action.js:

const actions = {
    TEST: 'TEST',
    TEST_DONE: 'TEST_DONE',

    callTest: (id) => ({
        type: actions.TEST,
        payload: {
            id
        }
    }),
};
export default actions;

reducer.js:

const initState = {
    testState:  {
        id: 0
    }
};

export default function TestReducers ( state=initState, { type, ...action}) {
    switch(type) {
    default:
        return state;
    case actions.TEST: {
        const { id } = state.testState;
        const nextId = id + 1;
        return {
            ...state,
            testState: {
                ...state.testState,
                id: nextId
            }
        };
    };
    case actions.TEST_DONE: {
        const { id } = state.testState;
        const nextId = id + 1;
        return {
            ...state,
            testState: {
                ...state.testState,
                id: nextId
            }
        };
    }
};
};

console output from component getSnapshotBeforeUpdate

Image of result

Upvotes: 2

Views: 154

Answers (1)

azundo
azundo

Reputation: 6052

Summarizing my comments from the question:

The redux state is indeed being updated as you've seen, but a component is not guaranteed to render every intermediate state change based on the way react batches state changes. To test this you can try importing delay from redux-saga/effects and adding yield delay(1000); before calling yield put in TestSagaFunc so the two state updates don't get batched together.

This is just a trick to illustrate the effects of batching and almost certainly not what you want to do. If you need the intermediate state to be rendered you could dispatch TEST_DONE from the component being rendered with a useEffect (or componentDidUpdate) to ensure that the component went through one render cycle with the intermediate state. But there is no way to force your component to render intermediate reducer states that are batched together.

Upvotes: 1

Related Questions