Reputation: 21
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:
testState.id
= 0
TEST
actiontestState.id
= 1
TEST_DONE
actiontestState.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
Upvotes: 2
Views: 154
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