useCheapoSuspense React Hoook

This isn’t Meteor-specific, but it may be of some interest here, as Meteor works well with Apollo and React.

So the React team put has been working on React Suspense. I’ve heard it may be on hold for the moment and/or being refactored. So in the meantime I put together a hook called useCheapoSuspense. :slight_smile:

One of the things that React Suspense has as a goal is to make pages less janky by handling spinners better. Instead of instantly appearing and disappearing, there would be a delay and if the data is loaded fast enough they won’t appear at all. But if the page is still waiting for the data to load after that specified time, then the spinner would appear.

So here’s useCheapoSuspense to fulfill some of this goal in the meantime.

// https://overreacted.io/making-setinterval-declarative-with-react-hooks/
function useInterval(callback, delay) {
    const savedCallback = useRef();

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

    // Set up the interval.
    useEffect(() => {
        function tick() {
            savedCallback.current();
        }

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

function useCheapoSuspense(setShowSpinner) {
    const keepWaitingBeforeShowingSpinner = useRef(true);
    const SHOW_SPINNER_AFTER_THIS_MANY_MS = 600;
    const timeElapsed = useRef(0)
    const timeStarted = useRef(new Date().getTime());
    useInterval(() => {
        timeElapsed.current = new Date().getTime() - timeStarted.current;
        keepWaitingBeforeShowingSpinner.current = timeElapsed.current < SHOW_SPINNER_AFTER_THIS_MANY_MS;
        if (!keepWaitingBeforeShowingSpinner.current) {
            setShowSpinner(true);
        }
        console.log('keepWaitingBeforeShowingSpinner.current == ', keepWaitingBeforeShowingSpinner.current);
    }, keepWaitingBeforeShowingSpinner.current ? 50 : null);
}

Example usage:

    const [showSpinner, setShowSpinner] = useState(false);
    useCheapoSuspense(setShowSpinner);

    const {data, error, loading, subscribeToMore, refetch} = useQuery(MY_QUERY, {
        variables: {"originatingUserId": Meteor.userId()},
    });

    if (loading) return (
        <>
            {showSpinner &&
            <MyShowSpinnerComponent/>
            }
        </>
    );

Please post improvements / suggestions / changes.

1 Like