I am using Suspense from React, and Await from React-Router-DOM to wait for a promise and return JSX on resolve, but in most cases I want to call some functions like navigate or something else, then the functions are called twice. I want to prevent that.
Loader
export const Loader = async ({ params }) => {
CheckAuth();
let fetch_data = fetchApi('auth/activate-account', 'POST', { verify_token: params.verify_token });
return defer({ loaderPromise: fetch_data });
}
// this loader is called in the Route loader prop
const VerifyToken = () => {
const loaderData = useLoaderData();
const navigate = useNavigate();
const { showAlert } = useGeneral();
return (
<React.Suspense fallback={<PreLoader />}>
<Await resolve={loaderData.loaderPromise}>
{(resolvedData) => {
if (resolvedData.type === "warning") {
showAlert(resolvedData.type, resolvedData.msg);
navigate('/Auth/Login', { replace: true });
return null;
}
return (
{ JSX }
);
}}
</Await>
</React.Suspense>
);
};
This causes calling the navigate and showAlert functions twice. I want to prevent that or use another approach or other methods.
I tried this:
let loaderRef = React.useRef(false);
<React.Suspense fallback={<PreLoader />}>
<Await resolve={loaderData.loaderPromise}>
{(resolvedData) => {
if (!loaderRef.current) {
showAlert(resolvedData.type, resolvedData.msg);
loaderRef.current = true;
setTimeout(() => {
navigate('/Auth/Login', { replace: true });
}, 0);
return null;
}
}}
</Await>
</React.Suspense>
This works but not efficient in most cases.
I suspect there is some rendering issue being exposed by a
React.StrictModecomponent higher up the ReactTree, specific lifecycle functions are double-invoked in non-production builds to expose logical issues in your code like unintentional side-effects.Abstract the unintentional side-effect of showing the alert and navigating into a React component that can use the
useEffecthook to correctly issue an intentional side-effect to show the alert and conditionally redirect or render the specified JSX content.Example: