Search code examples
reactjsreact-hooksnext.jsuse-effect

How do I create an interval API call in Nextjs?


const LoadingPage = ({merchantId, order}) => {

    const router = useRouter();

    useEffect(() => {
        if (order.status === "merchantAccepted") {
            router.push(`/${merchantId}/success`);
        }
    }, []);


    return (
        <Column>
            Waiting merchant to accept...
        </Column>
    );
};

export default LoadingPage;


export const getServerSideProps = async (ctx) => {
    const {params} = ctx;
    const {merchantId} = params;

    const merchant = await getMerchant(merchantId);
    const order = await getOrders("");

    return {
        props: {
            merchantId,
            merchant: merchant,
            order
        }
    };
};

What I am trying to do is waiting for merchant to accept the order which change the status of the order in db. Then I would like to fetch that status which will be use to redirect user to another page. At the moment I am using getServerSideProps but this method only fetch it once and I need to refresh the page. How do I make the API calls without refreshing the page?

Thank you.


Solution

  • There are a couple things working against you here, but it's pretty easy to solve!

    The first issue is in your effect. It only runs once instead of any time the order status changes. Super easy to fix by adding a dependency on order.status:

        useEffect(() => {
            if (order.status === "merchantAccepted") {
                router.push(`/${merchantId}/success`);
            }
        }, [order.status]);
    
    

    Now, to get the updated order information, we need to make some bigger changes. The first thing you'll want to do is cache the value of order in state so you can allow the component to call the API and update the state. The second thing is to pass in a way to update the order status. Combining the change to the effect hook plus those requirements, I came up with this for you:

    const LoadingPage = ({ getOrders, merchantId, order: orderProp }) => {
        const router = useRouter();
        const [order, setOrder] = useState(orderProp);
    
        useEffect(() => {
            if (order.status === "merchantAccepted") {
                router.push(`/${merchantId}/success`);
            }
        }, [order.status]);
    
        useEffect(() => {
            // skip if merchant already accepted
            if (order.status === "merchantAccepted") return;
    
            // keep track of the timeout so we can cancel
            let timeout;
    
            // create an internal function to call the API to check for updates
            const checkApi = async () => {
                const updatedOrder = await getOrders("");
                setOrder(updatedOrder);
    
                if (updatedOrder.status !== "merchantAccepted") {
                    // wait 2 seconds to call again
                    timeout = setTimeout(checkApi, 2000);
                }
            }
    
            // make the initial check in 2 seconds
            timeout = setTimeout(checkApi, 2000);
    
            // on unmount, make sure we cancel
            return () => {
                clearTimeout(timeout);
            }
        }, []); // only execute this effect on mount
    
        return (
            <Column>
                Waiting merchant to accept...
            </Column>
        );
    };
    
    export default LoadingPage;
    
    export const getServerSideProps = async (ctx) => {
        const {params} = ctx;
        const {merchantId} = params;
    
        const merchant = await getMerchant(merchantId);
        const order = await getOrders("");
    
        return {
            props: {
                merchantId,
                merchant: merchant,
                order,
                getOrders
            }
        };
    };
    

    I think this will solve it for you. Let me know if you have any questions!