React hook to wait until the previous call is complete

Viewed 6060

I am working on a react project which uses hooks. And I was assigned a task

"change the useInterval hook, or create a new one (usePoll?). This should operate the same as useInterval, but should wait until the ajax request is complete before starting the timer".

I am new to react hooks and was looking for a solution for this but could not find. Current useInterval function is as follows.

import React, { useEffect, useRef } from 'react';

export function useInterval(callback, delay, immediate = true) {
    const savedCallback = useRef();

    // Remember the latest callback.
    useEffect(() => {
        savedCallback.current = callback;
    }, [callback]);

    // Set up the interval.
    useEffect(() => {
        function tick() {
            savedCallback.current();
        }
        if (delay !== null) {
            if (immediate) {
                tick();
            }

            let id = setInterval(tick, delay);
            return () => clearInterval(id);
        }
    }, [delay]);
}

and it use in the program as follows.

useInterval(() => {
        get(`/api/v1/streams/1`).then(({ data: { data } }) => {
            setStream(data);
        });
    }, 5000);

and I need to change the useInterval function to wait until the ajax request is complete before starting the timer. It would be great if anyone can help me on this. Thanks

2 Answers

Give this a shot.. it requires calling the next function inside of then but it should come close to what you're looking for.

function useInterval(handler, delay, immediate = true) {
  React.useEffect(() => {
    let interval

    const start = () => {
      clearInterval(interval)
      interval = setInterval(() => handler(start), delay)
    }

    handler(start)

    return () => clearInterval(interval)
  }, [])
}

usage:

useInterval((next) => {
    get('/api/v1/streams/1').then(data => {
        // tell the timer to begin
        next()
    })
}, 5000)

You can use async\await to await for first call completes.

Modify internal useEffect like so

export function useInterval(callback, delay, immediate = true) {
    const savedCallback = useRef();

    // Remember the latest callback.
    useEffect(() => {
        savedCallback.current = callback;
    }, [callback]);
    // Set up the interval.
    useEffect(() => {
        // useEffect doesn't like async callbacks (https://github.com/facebook/react/issues/14326) so create nested async callback
        (async () => { 
            // Make tick() async
            async function tick() {
                await savedCallback.current();
            }
            if (delay !== null) {
                if (immediate) {
                    await tick();  // Here we should await for tick()
                }

                let id = setInterval(tick, delay);  // Unfortunately setInterval is not async/await compatible. So it will not await for tick
                return () => clearInterval(id);
           }
        })(); // Call nested async function
    }, [delay]);

}

And you callback should return Promise so async\await to work properly

useInterval(() => {
    // After .then promise will be resolved, so our useInterval will know about that
    return get(`/api/v1/streams/1`).then(({ data: { data } }) => {
        setStream(data);
    });
}, 5000);
Related