我正在制作一个 React/Typescript 项目,即使我遵循所有 React Hooks 规则(至少是我理解它们的方式),它也会给我这个错误。
<块引用>错误:无效的挂钩调用。 Hooks 只能在 body 内部调用 一个功能组件。这可能发生在以下情况之一 原因:
问题出在地址组件的第一行和第二行
应用程序工作正常,除非加载组件 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;