首页 > 解决方案 > 为什么我的自定义钩子被调用了这么多次?

问题描述

我正在尝试实现一个自定义钩子来为应用程序提供访客购物车。我的钩子环绕着useMutation来自 Apollo 的钩子,它将购物车 ID 保存在 cookie 中,同时还提供了“重置”购物车的功能(基本上,在下订单时删除 cookie)。

代码时间!(为简洁起见省略了一些代码):

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];
};

在我的组件中,我只是使用let [cartId, resetCart] = useGuestCart();.

当我运行单元测试(使用 Apollo 提供模拟突变)时,我看到钩子被调用了几次,输出看起来像这样:

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!

我只是开始使用钩子,所以我仍然无法掌握它们的工作方式。为什么这么多的钩子调用?

谢谢您的回复!

标签: javascriptreact-hooksreact-apollo-hooks

解决方案


将钩子视为直接在组件中具有相同的代码。这意味着每次组件渲染钩子都会运行。

例如,您定义:

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

语句中的内容将在每次cartId创建时在每次渲染时运行,并且此时没有分配任何值。而不是使用if语句使用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];
};

还要注意,useCallback如果接收函数的组件没有被记忆,使用并没有实际的好处。


推荐阅读