即使遵循所有反应钩子规则,也要反应“无效的钩子调用”

时间:2021-07-06 01:45:50

标签: reactjs react-hooks react-typescript

我正在制作一个 React/Typescript 项目,即使我遵循所有 React Hooks 规则(至少是我理解它们的方式),它也会给我这个错误。

<块引用>

错误:无效的挂钩调用。 Hooks 只能在 body 内部调用 一个功能组件。这可能发生在以下情况之一 原因:

  1. 您的 React 和渲染器版本可能不匹配(例如 React DOM)
  2. 你可能违反了钩子规则
  3. 您可能在同一个应用中拥有多个 React 副本,请参阅 https://reactjs.org/link/invalid-hook-call 以获取有关如何调试的提示 并解决这个问题。

问题出在地址组件的第一行和第二行

应用程序工作正常,除非加载组件 Address.tsx

Address.tsx

import React, { useState, useRef } from "react";
import { RouteComponentProps } from "react-router-dom";
import getUserAddressByCEP, { BrazilianAddress, validateCEP } from "./cep";
import { LeftContainer, FormAddress, AddressDetailsContainer } from "./checkout.styles";

interface props extends RouteComponentProps<any> {}

const Address: React.FC<props> = ({ history }) => {
    const [userAddress, setUserAddress] = useState<BrazilianAddress | null>(null);
    const inputCEP = useRef<HTMLInputElement>(null);

    const findUserAddress = async () => {
        if (!inputCEP.current) return;

        let isValidCEP = validateCEP(inputCEP.current.value);
        if (isValidCEP) setUserAddress(await getUserAddressByCEP(inputCEP.current.value));
    };

    return (
        <>...</>
    );
};

export default Address;

根据我在 Google 上对这个问题的搜索,我...

奇怪的是,我有另一个具有相同结构的文件(下面的代码)运行良好。

import React, { useEffect, useState } from "react";
import { RouteComponentProps, Route, Switch } from "react-router-dom";

import { ReactComponent as DeliveryIcon } from "../../media/icons/003-delivery.svg";
import { ReactComponent as ChequeIcon } from "../../media/icons/042-cheque.svg";

import {
    CheckoutContainer,
    CheckoutHeader,
    CheckoutHeaderDot,
    OrderDetails,
    OrderProducts,
    GlobalStyle,
    ContainerEmptyCarMessage,
} from "./checkout.styles";

import CartItem from "../../components/Cart/CartItem";
import Address from "./Address";
import Payment from "./Payment";
import { useDispatch, useSelector } from "react-redux";
import { CartProduct, CartProductsState, DuxCartProductAction } from "../../redux/cartReducer";
import { convertToReal } from "../../utils";
import { scrollPageToTop } from "../../App";

interface props extends RouteComponentProps<any> {}

const Checkout: React.FC<props> = ({ history }) => {
    const checkoutProducts = useSelector<CartProductsState, CartProductsState["products"]>(
        (state: CartProductsState) => state.products,
    );

    const [orderFinished, setOrderFinished] = useState(false);

    const dispatch = useDispatch();

    const getCartProduct = (productId: number): CartProduct | null => {
        let auxProduct = null;
        checkoutProducts.forEach((product) => {
            if (product.productId === productId) auxProduct = product;
        });
        return auxProduct;
    };

    const finishOrder = () => {
        let action: DuxCartProductAction = {
            type: "@cart/CLEAR_CART",
        };
        setOrderFinished(true);
        dispatch(action);
        window.location.href = "/";
    };

    const increaseProductQuantity = (productId: number): void => {
        let productToIncrease = getCartProduct(productId);
        if (productToIncrease === null) return;

        let auxAction: DuxCartProductAction = {
            type: "@cart/INCREASE_PRODUCT_QUANTITY",
            data: productToIncrease,
        };
        dispatch(auxAction);
    };

    const decreaseProductQuantity = (productId: number): void => {
        let productToIncrease = getCartProduct(productId);
        if (productToIncrease === null) return;

        let auxAction: DuxCartProductAction = {
            type: "@cart/DECREASE_PRODUCT_QUANTITY",
            data: productToIncrease,
        };
        dispatch(auxAction);
    };

    if (history.location.pathname !== "/checkout/address" && history.location.pathname !== "/checkout/payment") {
        history.push("/error-404");
    }

    useEffect(() => {
        setTimeout(scrollPageToTop, 100);
    }, []);

    if (checkoutProducts.length === 0 && !orderFinished) {
        setTimeout(() => (window.location.href = "/"), 3500);
        return (
            <ContainerEmptyCarMessage>
                <h2>
                    Seu carrinho está vazio, adicione alguns itens antes de vir para esta página. Você será redirecionado(a) para
                    a página inicial.
                </h2>
            </ContainerEmptyCarMessage>
        );
    }

    let total_order = convertToReal.format(
        checkoutProducts.reduce((sum, product) => sum + product.price * product.quantity, 0),
    );

    return (
        <CheckoutContainer className="container" id="checkout_page_container">
            <CheckoutHeader>
                <span id="checkout__header_line"></span>
                <CheckoutHeaderDot id="checkout__header_dot_address">
                    <span>Endereço de entrega</span>
                    <div className="checkout__header_dot active_checkout_dot">
                        <DeliveryIcon />
                    </div>
                </CheckoutHeaderDot>
                <CheckoutHeaderDot id="checkout__header_dot_payment">
                    <span>Pagamento</span>
                    <div
                        className={`checkout__header_dot ${
                            history.location.pathname === "/checkout/payment" ? " active_checkout_dot" : ""
                        }`}
                    >
                        <ChequeIcon />
                    </div>
                </CheckoutHeaderDot>
            </CheckoutHeader>
            <Switch>
                <Route path="/checkout/address" render={Address} />
                <Route
                    path="/checkout/payment"
                    render={(props: RouteComponentProps) => <Payment {...props} finishOrder={finishOrder}></Payment>}
                />
            </Switch>

            <OrderDetails>
                <div id="order_details_header">
                    <span id="checkout__total_label">Total</span>
                    <span id="checkout__total_value">{total_order}</span>
                </div>
                <span className="normal_span" id="buying_message">
                    Você está comprando:
                </span>
                <OrderProducts>
                    {checkoutProducts.map((product, i) => (
                        <CartItem
                            key={i}
                            productId={product.productId}
                            name={product.name}
                            image={product.image}
                            price={product.price}
                            size={product.size}
                            color={product.color}
                            quantity={product.quantity}
                            increaseProductQuantity={increaseProductQuantity}
                            decreaseProductQuantity={decreaseProductQuantity}
                        />
                    ))}
                </OrderProducts>
            </OrderDetails>
            <GlobalStyle />
        </CheckoutContainer>
    );
};

export default Checkout;

0 个答案:

没有答案