Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my custom hook called so many times?

I'm trying to implement a custom hook to provide the app with a guest shopping cart. My hook wraps around the useMutation hook from Apollo and it saves the shopping cart id in a cookie while also providing a function to "reset" the cart (basically, to remove the cookie when the order is placed).

Code time! (some code omitted for brevity):

export const useGuestCart = () => {
    let cartId;
    const [createCart, { data, error, loading }] = useMutation(MUTATION_CREATE_CART);
    console.log(`Hook!`);

    if (!cartId || cartId.length === 0) {
        createCart();
    }
    if (loading) {
        console.log(`Still loading`);
    }
    if (data) {
        console.log(`Got cart id ${data.createEmptyCart}`);
        cartId = data.createEmptyCart;
    }

    const resetGuestCart = useCallback(() => {
        // function body here
    });

    return [cartId, resetGuestCart];
};

In my component I just get the id of the cart using let [cartId, resetCart] = useGuestCart(); .

When I run my unit test (using the Apollo to provide a mock mutation) I see the hooked invoked several times, with an output that looks something like this:

console.log src/utils/hooks.js:53
    Hook!

  console.log src/utils/hooks.js:53
    Hook!

  console.log src/utils/hooks.js:59
    Still loading

  console.log src/utils/hooks.js:53
    Hook!

  console.log src/utils/hooks.js:62
    Got cart id guest123

  console.log src/utils/hooks.js:53
    Hook!

  console.log src/utils/hooks.js:53
    Hook!

I'm only getting started with hooks, so I'm still having trouble grasping the way they work. Why so many invocations of the hook?

Thank you for your replies!

like image 838
Daniel Platon Avatar asked Sep 19 '19 09:09

Daniel Platon


People also ask

Are custom Hooks called on every render?

Calls to Hooks are either inside a PascalCase function (assumed to be a component) or another useSomething function (assumed to be a custom Hook). Hooks are called in the same order on every render.

Can a custom hook call another custom hook?

A hook should always be placed at the top level of your component. Hooks cannot work from a regular function. So, don't call a hook from a regular function. A custom Hook can invoke another Hook.

Do custom Hooks cause Rerender?

Every state change in a hook, whether it affects its return value or not, will cause the “host” component to re-render.

Do custom Hooks have to start with use?

A custom Hook is a JavaScript function that begins with use. It is not mandatory to begin the custom Hook name with “use,” but without it, React would be unable to check for violations of the Hooks rules automatically. Therefore, it is critical to adhere to that naming convention.


1 Answers

Think of hooks as having that same code directly in the component. This means that every time the component renders the hook will run.

For example you define:

let cartId;
// ...
if (!cartId || cartId.length === 0) {
    createCart();
}

The content inside the statement will run on every render as cartId is created every time and it doesn't have any value assigned at that point. Instead of using if statements use useEffect:

export const useGuestCart = () => {
    const [cartId, setCartId] = useState(0);
    const [createCart, { data, error, loading }] = useMutation(
        MUTATION_CREATE_CART
    );
    const resetGuestCart = () => {
        // function body here
    };

    useEffect(() => {
        if(!cartId || cartId.length === 0){
            createCart();
        }
    }, [cartId]);

    useEffect(() => {
        // Here we need to consider the first render.
        if (loading) {
            console.log(`Started loading`);
        } else {
            console.log(`Finished loading`);
        }
    }, [loading]);

    useEffect(() => {
        // Here we need to consider the first render.
        console.log(`Got cart id ${data.createEmptyCart}`);
        setCartId(data.createEmptyCart);
    }, [data]);

    return [cartId, resetGuestCart];
};

Also notice that there is no actual benefit from using useCallback if the component which is receiving the function is not memoized.

like image 104
Alvaro Avatar answered Oct 19 '22 09:10

Alvaro