shanky
shanky

Reputation: 801

Reducer isn't updating state

I can't get reducer to return updated state. It returns the updated state with correct info on 2nd time button click. workflow is the state which has tasks: [], taskId: '' and hasError variable. On button click I call onUnassign dispatch action which returns an error to invoke WORKFLOW_VALIDATION_DATA_ERROR reducer action which updates state variable hasError to true but hasError displays true on 2nd button click. I'm not able to figure it out why? Below is my code:

const DisActionConnected: React.FC = () => {            
  const dispatch = useDispatch();
  const {tasks, workflow} = useSelector((state: IRootState) => state.workflow);
            
  const onUnassign = () => {
    if (tasks && tasks.length > 0) {
      dispatch(setUnassign(name));
      console.log(workflow) // here hasError is true on 2nd time not first time
    }
  };
            
  return (
    <div>
      <DisActions
        userId={userId}
        onUnassign={onUnassign}
      />
    </div>
  );
};

store action

export const setUnassign = (name: string) => {            
  return {
    type: TASK_UNASSIGN_PENDING,
    payload: {
      name
    }
  }
};

reducer function

export function workflowReducer(
  state: IWorkflowState = initialState,
  action: WorkflowActionsType
): IWorkflowState {
  switch(action.type) {
    case WORKFLOW_VALIDATION_DATA_ERROR:
      return {...state, hasError: true};
    case TASK_UNASSIGN_PENDING:
      return {...state};
    default:
      return {...state};
  };
}

STATE

export interface IWorkflowState {
  tasks: ITask[];
  taskId: string;
  hasError: boolean | null
};
        
const initialState: IWorkflowState = {
  tasks: [],
  taskId: '',
  hasError: false
};
export function* setUnassignDis() {    
  const errorMessage = "error saving data"
  yield put({
    type: WORKFLOW_VALIDATION_DATA_ERROR,
    payload: {errorMessage} 
  });
}

function* workflowSaga() {
  yield takeEvery(WorkflowStore.TASK_UNASSIGN_PENDING, setUnassignDis);
}

Upvotes: 1

Views: 44

Answers (1)

Drew Reese
Drew Reese

Reputation: 202979

React state updates are not instantaneous and this is how Javascript closures work. workflow can never be anything but what the workflow state value is when onUnassign is called. The current workflow value is closed over in callback scope. When the button is clicked again and the onUnassign callback is called it closes over the updated state from the previous update.

If you want to log updated state values then use the useEffect hook with appropriate dependency.

Example:

const {
  tasks,
  workflow
} = useSelector((state: IRootState) => state.workflow);

// Log when workflow state updates
React.useEffect(() => {
  console.log(workflow);
}, [workflow]);

const onUnassign = () => {
  if (tasks?.length) {
    dispatch(setUnassign(name));
  }
};

...

Upvotes: 1

Related Questions