Search code examples
reactjsreduxredux-saga

Redux useSelector is not updated fast enough


I use redux with saga in my project, to make Axios requests in the store. In order to give the user feedback about if the request has succeeded or not, I store an object in my store that holds two properties - errMsg and isFetching.

Whenever I make a request I set isFetching to true, and then if I the request is successful, I trigger a success action which changes isFetching to false. I listen to isFetching changes in my component, if the last state of isFetching was false and now it changed to true, and there's no error message, then I trigger a success modal.

This implementation worked for me with a POST request, but when I tried it with a DELETE request, isFetching never changed to true.

I tried debugging redux, and saw that the two actions were happening and that the store was changing, but the effect in my component wasn't triggered.

I added a delay between the changing of isFetching to true, and then it worked.

Redux changes are too fast that useSelector isn't able to get the changes? is there a solution for this?

(I'm sorry for explaining the code, my project is in a closed network and I can't copy code here, if the explanation isn't good enough, please tell me and I'll try to manually write the code)

EDIT:

Adding code example:

The listener for changes in isFetching:

 const { isFetchingDelete, errMsgDelete } = useSelector(
    (state: StoreState) => ({
      isFetchingDelete: state.students.deleteState.isFetching,
      errMsgDelete: state.students.deleteState.errMsg,
    })
  );

 const prevIsFetchingDeleteRef = useRef<boolean>();

.........
  useEffect(() => {
    // trying to log the value of isFetchingDelete here, always logs false
    if (!isFetchingDelete && prevIsFetchingDeleteRef.current) {
      if (errMsgDelete) {
        Swal.fire();
      } else {
        Swal.fire();
      }
    }

    prevIsFetchingDeleteRef.current = isFetchingDelete;
  }, [errMsgDelete, isFetchingDelete]);

The saga:

  function* deleteStudentHandler(action: ActionType<typeof deleteStudent>) {
    try {
      yield call(StudentsService.delete, action.payload);
      // if I add a delay of 1000ms here it's working
      yield put(deleteStudentSuccess(action.payload));
    } catch (e) {
      yield put(deleteStudentFailure(e.message));
    }
  }

my reducer:

  case: StudentsActionTypes.DELETE_STUDENT: {
    return {
      ...state,
      deleteState: {
        ...state.deleteState,
        isFetching: true
      }
    }
  }

  case: StudentsActionTypes.DELETE_STUDENT_SUCCESS: {
    return {
      ...state,
      students: [
        ...state.students.filter((student) => student.id !== action.payload),
      ],
      deleteState: {
        ...state.deleteState,
        isFetching: false,
      }
    }
  }

The code above works just fine on a post request that takes 300ms, but on a delete request that takes 2ms (json-server) it doesn't work. Adding where I stated in a comment in the code, makes the code work.


Solution

  • I finally understood why this was happening.

    here, my problem was that StudentService.delete wasn't returning the Axios response, thus not returning a promise, and saga didn't have nothing to wait for..

      function* deleteStudentHandler(action: ActionType<typeof deleteStudent>) {
        try {
          yield call(StudentsService.delete, action.payload);
          // if I add a delay of 1000ms here it's working
          yield put(deleteStudentSuccess(action.payload));
        } catch (e) {
          yield put(deleteStudentFailure(e.message));
        }
      }
    

    hopefully this helps other people that may encounter this problem.