Search code examples
javascriptreactjsreduxredux-toolkitrtk-query

Redirect to the beginning page unsuccessful


I am using Redux-Toolkit (RTK) Query to log a user into my React application, and when the user clicks the "logout" button, it should redirect to the home page, "/". However, when I click "logout" button, the page doesn't redirect to the home page.

In my Layout.jsx, I use useEffect hook to check whether the mutation succeeded

const navigate = useNavigate();
const [sendLogout, { isLoading, isSuccess, isError, error }] =
  useSendLogoutMutation();

useEffect(() => {
  if (isSuccess) {
    console.log("success");
    navigate("/");
  }
}, [isSuccess, navigate]);

In my authApiSlice.js, when I comment out dispatch(apiSlice.util.resetApiState());, the isSuccess value in the code above works, however, if I add the code dispatch(apiSlice.util.resetApiState());, it doesn't work again.

import { apiSlice } from "../../app/api/apiSlice";
import { logOut } from "./authSlice";

export const authApiSlice = apiSlice.injectEndpoints({
  endpoints: (builder) => ({
    login: builder.mutation({
      query: (credentials) => ({
        url: "/auth",
        method: "POST",
        body: { ...credentials },
      }),
    }),
    sendLogout: builder.mutation({
      query: () => ({
        url: "/auth/logout",
        method: "POST",
      }),
      async onQueryStarted(arg, { dispatch, queryFulfilled }) {
        try {
          const { data } = await queryFulfilled;
          //   console.log(data);
          dispatch(logOut());
          dispatch(apiSlice.util.resetApiState());
        } catch (err) {
          console.log(err);
        }
      },
    }),
    refresh: builder.mutation({
      query: () => ({
        url: "/auth/refresh",
        method: "GET",
      }),
    }),
  }),
});

export const {
  useLoginMutation,
  useSendLogoutMutation,
  useRefreshMutation
} = authApiSlice;

I don't understand what I did wrong.


Solution

  • I don't think you did anything overtly wrong, per se, but resetting the API slice state during a mutation request processing probably isn't the best thing to do since this appears to clear in-flight requests and their associated metadata.

    That said, you don't need to wait for the mutation endpoint to complete and trigger the component using the useSendLogoutMutation to rerender so the mutation results are accessible, the sendLogout trigger function returns a Promise that be used to run additional logic after a successful mutation call. I suspect this could work for you here.

    See useMutation signature:

    type UseMutation = (
      options?: UseMutationStateOptions
    ) => [UseMutationTrigger, UseMutationResult | SelectedUseMutationResult]
    
    type UseMutationTrigger<T> = (arg: any) => Promise<
      { data: T } | { error: BaseQueryError | SerializedError }
    > & {
      requestId: string // A string generated by RTK Query
      abort: () => void // A method to cancel the mutation promise
      unwrap: () => Promise<T> // A method to unwrap the mutation call and provide the raw response/error
      reset: () => void // A method to manually unsubscribe from the mutation call and reset the result to the uninitialized state
    }
    

    Invoke the sendLogout trigger function and await its unwrapped result.

    Example:

    const navigate = useNavigate();
    const [
      sendLogout,
      { isLoading, isError, error }
    ] = useSendLogoutMutation();
    
    ...
    
    const logoutHandler = async () => {
      try {
        await sendLogout().unwrap(); // <-- trigger mutation and await success
    
        // Success, redirect to home
        navigate("/", { replace: true });
      } catch(error) {
        // handle/ignore any thrown errors or Promise rejections
      }
    };
    
    ...
    

    If there's still an issue with resetting the API slice from within the sendLogout endpoint then my suggestion would be to reset it after the mutation succeeds. Similar to above.

    ...
    sendLogout: builder.mutation({
      query: () => ({
        url: "/auth/logout",
        method: "POST",
      }),
      async onQueryStarted(arg, { dispatch, queryFulfilled }) {
        try {
          const { data } = await queryFulfilled;
          dispatch(logOut());
        } catch (err) {
          console.log(err);
        }
      },
    }),
    ...
    
    const dispatch = useDispatch();
    const navigate = useNavigate();
    const [
      sendLogout,
      { isLoading, isError, error }
    ] = useSendLogoutMutation();
    
    ...
    
    const logoutHandler = async () => {
      try {
        await sendLogout().unwrap();
    
        // Success, reset API state and redirect to home
        dispatch(apiSlice.util.resetApiState()); // <-- reset after success
        navigate("/", { replace: true });
      } catch(error) {
        // handle/ignore any thrown errors or Promise rejections
      }
    };
    
    ...