React custom hook not rendering

Advertisements

I am building an app with React using Remix, fly.io for deployment. I have a custom React hook useCountdown that has the following code:

import { useEffect, useState } from 'react';

const useCountdown = (targetSeconds) => {
    const countDownSeconds = targetSeconds

    const [countDown, setCountDown] = useState(countDownSeconds);

    useEffect(() => {
        const interval = setInterval(() => {
            setCountDown(countDownSeconds);

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

        return getReturnValues(countDown);
    }, [])
};

const getReturnValues = (countDown) => {
    const seconds = Math.floor((countDown % (1000 * 60)) / 1000);

    return [seconds];
}

export { useCountdown }


The DateTimeDisplay component has the following code and is a component dependency of the hook:

import React from 'react';

const DateTimeDisplay = ({ value, type, isDanger }) => {
    return (
        <div className={isDanger ? 'countdown danger' : 'countdown'}>
            <p>{value}</p>
            <span>{type}</span>
        </div>
    );
};

export default DateTimeDisplay;

Finally, I have a CountdownTimer component that has the following code:

import React from 'react';
import DateTimeDisplay from './DateTimeDisplay';
import ExpiredNotice from './ExpiredNotice'
import { useCountdown } from '../hooks/useCountdown';


const ShowCounter = ({ seconds }) => {
    return (
        <div className="show-counter">
                <DateTimeDisplay value={seconds} type={'seconds'} isDanger={false} />
        </div>
    );
};

const CountdownTimer = ({ targetSeconds }) => {
    const [seconds] = useCountdown(targetSeconds);

    if (seconds <= 0) {
        return <ExpiredNotice />;
    } else {
        return (
            <ShowCounter
                seconds={seconds}
            />
        );
    }
};

export default CountdownTimer;

Upon trying to utilize the useCountdown() hook, I get the following error:

TypeError: useCountdown is not a function or its return value is not iterable
    at CountdownTimer (/Users/tduke/Desktop/dev/drawesome/app/components/CountdownTimer.jsx:17:23)
    at processChild (/Users/tduke/Desktop/dev/drawesome/node_modules/react-dom/cjs/react-dom-server.node.development.js:3353:14)
    at resolve (/Users/tduke/Desktop/dev/drawesome/node_modules/react-dom/cjs/react-dom-server.node.development.js:3270:5)
    at ReactDOMServerRenderer.render (/Users/tduke/Desktop/dev/drawesome/node_modules/react-dom/cjs/react-dom-server.node.development.js:3753:22)
    at ReactDOMServerRenderer.read (/Users/tduke/Desktop/dev/drawesome/node_modules/react-dom/cjs/react-dom-server.node.development.js:3690:29)
    at renderToString (/Users/tduke/Desktop/dev/drawesome/node_modules/react-dom/cjs/react-dom-server.node.development.js:4298:27)
    at handleRequest (/Users/tduke/Desktop/dev/drawesome/app/entry.server.jsx:10:16)
    at handleDocumentRequest (/Users/tduke/Desktop/dev/drawesome/node_modules/@remix-run/server-runtime/server.js:400:18)
    at requestHandler (/Users/tduke/Desktop/dev/drawesome/node_modules/@remix-run/server-runtime/server.js:49:18)
    at /Users/tduke/Desktop/dev/drawesome/node_modules/@remix-run/express/server.js:39:22

The line in question:

    const [seconds] = useCountdown(targetSeconds);

  1. Can someone explain to me this error, and what it exactly is telling me so I understand this error in it’s entirety, and what the cause is in this instance?

  2. How do I fix it?

>Solution :

useCountdown does not have a return statement, so it’s implicitly returning undefined. Then when you try to destructure undefined, you get that error because array destructuring only works on arrays (or other iterables, which is why the error mentions "iterable"s, not arrays)

You did put a return statement inside your useEffect, but returning something in a useEffect is for cleaning up the effect. It won’t cause useCountdown to return anything. To fix this, move your return statement to the body of useCountdown, and move the effect cleanup to be the return from the effect:

const useCountdown = (targetSeconds) => {
    const countDownSeconds = targetSeconds

    const [countDown, setCountDown] = useState(countDownSeconds);

    useEffect(() => {
        const interval = setInterval(() => {
            setCountDown(countDownSeconds);
        }, [countDownSeconds]);

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

    return getReturnValues(countDown);
};

Leave a ReplyCancel reply