Search code examples
reactjsnext.jsuse-effectreact-contextnext-router

Next.js useRouter with useEffect pushes even if condition is met on page refresh


I am running into an issue with useRouter and useEffect where I want to re-direct the user if a certain condition is not met.

On page 1 you select an amount which is passed into a context and is retrieved on page 2.

On page 2 I have this condition:

import { useEffect, useContext } from "react";
import { useRouter } from "next/router";
import AppContext from "../context/AppContext";

const PageTwo = () => {
  const { amount } = useContext(AppContext);
  const router = useRouter();
  useEffect(() => {
    if (!amount || amount == null) {
      router.push("/page-1"); // redirect if no amount is selected
    }
  });

  return (
    ...
  );
};

When we land on page 2 I have the amount but when you do a page refresh it directs you back to page-1 even when there is an amount set. It works fine when you go straight to page-2, without selecting an amount on page-1, it re-directs you back to page-2.

I might be getting something wrong here in how the Context API and or useRouter and useEffect work in this case.

Here is a CodeSanBox example with the issue:

https://codesandbox.io/s/next-js-userouter-with-useeffect-6645u?file=/pages/page-2.js


Solution

  • That behaviour occurs because even after setting a new amount value in context, when reloading the page its initial value will be null on first render which happens before the app checks and sets the localStorage value. This triggers the redirect before the setAmount(amount) is called with the stored value from localStorage.

    To prevent this with minimal changes, you could modify the check that triggers the redirect to the following. Making sure to also add amount to the useEffect's dependencies array.

    useEffect(() => {
        if (amount === "") {
            router.push("/page-1");
        }
    }, [amount]);
    

    You'd also have to change the value passed to setAmount in _app to default to empty string if no value is stored.

    useEffect(() => {
        const amount = localStorage.getItem("amount") ?? "";
        setAmount(amount);
    }, []);
    

    This allows us to have a different values for the initial state value (null) and empty value (""). Meaning the redirect will not happen before amount is updated with the value stored in localStorage.

    Finally, to keep the logic consistent, you have to update the clearAmount function to set amount to the empty value.

    const clearAmount = () => {
        setAmount("");
    };