In React, how to redirect automatically after x second once password reset is successful - using react-router-dom?

Viewed 65

I'm new to React and have been struggling with this for a little while now and will appreciate some pointers. I've researched, but can't find a workable example to fit my use-case. I've read quite a few questions on similar topic, but none is with useMutation, and most deal with Classes and useHistory. When I try to use history, I get error that push function is not available. I'm using react-router-dom v5.

My react app is using functional components, it has a password reset page that displays a chip message after password is reset successfully. I want to redirect the user to login page x seconds after password is reset. I'm using useMutation and mutateAsync for reset logic. Once successful, I want to call the return <Redirect to='/login' /> type logic. In order to handle time delay, I'm using setTimeout(). My code for reset page is something like this:

  const [redirectNow, setRedirectNow] = useState<boolean>(false);

  const { mutateAsync, isLoading, isSuccess, error } = useMutation<
    void,
    error,
    {
      password: string;
    }
  >(submitPasswordRequest);

I've tried isSuccess and onSuccess, but they only redirect, if <Redirect> is without setTimeOut. Something like this doesn't work:

  if (isSuccess) {
    setTimeout(() => {
      console.log('OK');
      setRedirectNow('yes');
    }, 2000);
    return <Redirect to="/login" />;
  }

Nor something like this work:

if (isSuccess) {
    setTimeout(() => {
      console.log('OK');
      return <Redirect to="/login" />;
    }, 2000);
  }

After 2 seconds of delay, I get the console.log message, but no redirect.

I've also tried, onSuccess without any success:

const { mutateAsync, isLoading, isSuccess, error } = useMutation<
void,
error,
{
  password: string;
}
>(submitPasswordRequest, {
    onSuccess: () => {
      setTimeout(() => {
        setRedirectNow(true);
      },3000);
      return <Redirect to="/login" />;
    }
  });

I'll appreciate some guidance here.

3 Answers

I am not sure what the <Redirect /> component is, but with react-router-dom you can use the useNavigate() hook to navigate the page.

import { useHistory } from "react-router-dom";

let history = useHistory();

if (isSuccess) {
  setTimeout(() => {
    history.push('/login')
  }, 2000);
}

if you use "react-router-dom" v6, you can use "useNavigate" like this:

// import useNavigate
import { useNavigate } from "react-router-dom";

//make variable
const history = useNavigate();

//push to page you want
history.push("/login");

if you use "react-router-dom" v5, you can use "useHistory" like this:

// import useNavigate
import { useHistory } from "react-router-dom";

//make variable
const history = useHistory();

//push to page you want
history.push("/login");

Issue

Components like Redirect necessarily need to be rendered in order to effect any navigation. Returning Redirect to issue a declarative navigation from an asynchronous callback just won't work.

Solution

Use the history object to issue an imperative navigation.

Example:

const history = useHistory();
const timerRef = React.useRef();

const {
  mutateAsync,
  isLoading,
  isSuccess,
  error,
} = useMutation<void, error, { password: string }>(
  submitPasswordRequest,
  {
    onSuccess: () => {
      timerRef.current = setTimeout(() => {
        history.replace("/login");
      }, 2000);
    }
  },
);

React.useEffect(() => {
  return () => {
    // clear any running timers on component unmount
    clearTimeout(timerRef.current);
  };
}, []);
Related