问题
In our project we are using react-redux
with reselect
and redux-saga
In the store I have a selectedStageId
along with an array of stages
and a memoized selector that finds and returns the correct stage based on the id.
This selector is mapped to one of my components props as this.props.selectedStage
, and in one of my onClick handlers, I dispatch an action to update the selectedStageId
to the id of the newly selected UI item, and then attempt to pass the new selectedStage
to an edit method...
However, even though I have added breakpoints and verified that both my reducers and selectors are being called with the new id, the new selectedStage
value is NOT being updated synchronously on my component's props... instead this.props.selectedStage
still references the previous value...
Example:
onItemSelect = (stageId: number): void => {
// updateSelectedStageId() is a mapped dispatch method that updates
// selectedStageId in the store
this.props.updateSelectedStageId(stageId);
// I have debugged the above dispatched action and verified that
// both the reducer and selector are being called with the new
// stageId, and the selector is executing and returning the correct
// new stage object before the next line is processed...
this.editStage(this.props.selectedStage;); // <-- wrong value
// this.props.selectedStage is the mapped memoized selector that
// returns the target stage based on the selectedStageId, but
// here it still references the previous value, not the new one
}
I've read that redux is normally synchronous, but that redux-saga could somehow be making this asynchronous... However, since I've verified that both the reducer and selector are being called synchronously, it seems that the issue lies somewhere else, maybe in when connect actually updates mapped state to props...
Can anyone provide some insight on how to accomplish this the right way?
Thanks!
Josh
UPDATE
Here are the related bits for my reducer and selector...
reducer.ts
import produce from 'immer';
const initialState = {
selectedStageId: -1,
stages: []
}
export const appReducer = (state = initialState, action) => {
return produce(state, (draftState) => {
switch (action.type) {
case ActionType.UpdateSelectedStageId
draftState.selectedStageId = action.id;
break;
}
});
}
selectors.ts
import { createSelector } from 'reselect';
const _getSelectedStage = (stages, id) => {
return stages.find((s) => s.id === id);
};
export const selectedStage = createSelector(
[getStages, getSelectedStageId],
_getSelectedStage
);
回答1:
I had the same problem (almost the same actually) today.
Every state's change (redux or not) are asynchronous. You can't actually trust the value will be updated.
Triggering a synchronous action doesn't mean it's synchronous within the current rendering phase. So your app dispatch the action, finishes the rendering, then the new redux state triggers a re-render.
dispatch({type: CHANGE_VALUE, value : newValue})
console.log(value) //oldValue
The state get's updated synchronously, but React will only be aware of that change in the next render.
So, just use the value you are dispatching the action:
onItemSelect = (stageId: number): void => {
this.props.updateSelectedStageId(stageId);
this.editStage(stageId)
}
来源:https://stackoverflow.com/questions/57226154/react-redux-reselect-is-mapped-state-to-props-using-selectors-synchronous