如何解决CryptoExchange调用的Hooks顺序变化引发的错误?

huangapple go评论60阅读模式
英文:

How to solve an error for a change in the order of Hooks called by CryptoExchange?

问题

这是我的组件:

import { useEffect, useState } from "react";
import { Col, Row, Space, Select } from "antd";
import styles from "./CryptoExchange.module.css";

import bitcoin from "../../../assets/Bitcoin-3.svg";
import Image from "next/image";
import { DUMMY_TOKENS } from "@/src/api/api";
import { ICrypto } from "@/src/models/tokens";
import useTranslation from "next-translate/useTranslation";
import { useQuery } from "@tanstack/react-query";
import numeral from "numeral";
import Api from "@/src/api/real-api";

const numFormat = "0,0.00";
const crypto = "crypto";

const CryptoExchange = () => {
  const [selectedToken, setSelectedToken] = useState<ICrypto>(DUMMY_TOKENS[0]);
  const [currency, setCurrency] = useState<string>("USD");
  const [price, setPrice] = useState(0);
  const [high_24h, setHigh_24h] = useState(0);
  const [low_24h, setLow_24h] = useState(0);
  const [volume_24h, setVolume_24h] = useState(0);
  const [price_24h, setPrice_24h] = useState(0);

  const { data, isError, isLoading, error } = useQuery(
    [crypto],
    Api.fetchTokens
  );

  useEffect(() => {
    if (!isLoading && !isError && data) {
      exchangeHandler(currency, selectedToken);
    }
  }, [currency, selectedToken, data, isError, isLoading]);

  const exchangeHandler = (currency: string, token: ICrypto) => {
    if (currency === "USD") {
      setPrice(+token.tokenStat.price * 1);
      setPrice_24h(+token.tokenStat.price_24h * 1);
      setHigh_24h(+token.tokenStat.high_24h * 1);
      setLow_24h(+token.tokenStat.low_24h * 1);
      setVolume_24h(+token.tokenStat.volume_24h * 1);
    } else if (currency === "EUR") {
      setPrice(+token.tokenStat.price * 0.92);
      setPrice_24h(+token.tokenStat.price_24h * 0.92);
      setHigh_24h(+token.tokenStat.high_24h * 0.92);
      setLow_24h(+token.tokenStat.low_24h * 0.92);
      setVolume_24h(+token.tokenStat.volume_24h * 0.92);
    } else if (currency === "RSD") {
      setPrice(+token.tokenStat.price * 107.66);
      setPrice_24h(+token.tokenStat.price_24h * 107.66);
      setHigh_24h(+token.tokenStat.high_24h * 107.66);
      setLow_24h(+token.tokenStat.low_24h * 107.66);
      setVolume_24h(+token.tokenStat.volume_24h * 107.66);
    }
  };

  if (isLoading) return <h2>Loading...</h2>;
  if (isError)
    return (
      <>
        <h2>Something went wrong...</h2>
        <p>{error.toString()}</p>
      </>
    );

  const { t } = useTranslation("common");

  const tokensSelectOptions = data.map((el) => {
    return (
      <Select.Option key={el.id} value={JSON.stringify(el)} label={el.name}>
        {el.name}
      </Select.Option>
    );
  });

  const currencySelectOptions = [
    <Select.Option
      value="USD"
      label={`${selectedToken.symbol}/USD`}
      key={`${selectedToken.symbol}/USD`}
    >
      {`${selectedToken.symbol}/USD`}
    </Select.Option>,
    <Select.Option
      value="EUR"
      label={`${selectedToken.symbol}/EUR`}
      key={`${selectedToken.symbol}/EUR`}
    >
      {`${selectedToken.symbol}/EUR`}
    </Select.Option>,
    <Select.Option
      value="RSD"
      label={`${selectedToken.symbol}/RSD`}
      key={`${selectedToken.symbol}/RSD`}
    >
      {`${selectedToken.symbol}/RSD`}
    </Select.Option>,
  ];

  const handleSelectedToken = (value: any) => {
    const token = value && JSON.parse(value.value);
    setSelectedToken(token);
  };

  return (
    <div>
      <Row className={styles.gutterRow}>
        <Col md={19} sm={19} xs={15} className={styles.gutterCol}>
          <div className={styles.select}>
            <Image src={bitcoin} alt="bitcoin" className={styles.image} />
            <Space wrap>
              <Select
                defaultValue={{
                  value: data[0].name,
                  label: data[0].name,
                }}
                labelInValue={true}
                style={{ width: 100 }}
                onChange={(value) => handleSelectedToken(value)}
              >
                {tokensSelectOptions}
              </Select>

              <Select
                style={{ width: 100 }}
                onChange={(value) => setCurrency(value.value)}
                defaultValue={{
                  value: "USD",
                  label: `${selectedToken.symbol}/USD`,
                }}
                labelInValue={true}
              >
                {currencySelectOptions}
              </Select>
            </Space>
          </div>
          <div>
            <p className={styles.orangeP}>
              {numeral(price).format(numFormat)} {currency}
            </p>
          </div>
          <div>
            <div className={styles.changes}>
              <p className={styles.orangeP}>
                {numeral(price_24h).format(numFormat)}
              </p>
              <p className={styles.orangeP}>
                {numeral(selectedToken.tokenStat.change_24h).format(numFormat)}
              </p>
            </div>
            <h6>24h {t("exchange.changes")}</h6>
          </div>
          <div>
            <p>{numeral(high_24h).format(numFormat)}</p>
            <h6>24h {t("exchange.high")}</h6>
          </div>
          <div>
            <p>{numeral(low_24h).format(numFormat)}</p>
            <h6>24h {t("exchange.low")} </h6>
          </div>
          <div>
            <p>{numeral(volume_24h).format(numFormat)}</p>
            <h6>
              24h {t("exchange.volume")}({selectedToken.symbol})
            </h6>
          </div>
        </Col>
        <Col md={1} sm={1} xs={2} offset={1}>
          <button className={styles.btn}>{t("exchange.buy")}</button>
        </Col>
        <Col md={2} sm={2} xs={5} offset={1}>
          <button className={styles.btn}>{t("exchange.add")}</button>
        </Col>
      </Row>
    </div>
  );
};

export default CryptoExchange;

我在控制台中收到以下错误:
警告:

英文:

This is my component:

import { useEffect, useState } from &quot;react&quot;;
import { Col, Row, Space, Select } from &quot;antd&quot;;
import styles from &quot;./CryptoExchange.module.css&quot;;
import bitcoin from &quot;../../../assets/Bitcoin-3.svg&quot;;
import Image from &quot;next/image&quot;;
import { DUMMY_TOKENS } from &quot;@/src/api/api&quot;;
import { ICrypto } from &quot;@/src/models/tokens&quot;;
import useTranslation from &quot;next-translate/useTranslation&quot;;
import { useQuery } from &quot;@tanstack/react-query&quot;;
import numeral from &quot;numeral&quot;;
import Api from &quot;@/src/api/real-api&quot;;
const numFormat = &quot;0,0.00&quot;;
const crypto = &quot;crypto&quot;;
const CryptoExchange = () =&gt; {
const [selectedToken, setSelectedToken] = useState&lt;ICrypto&gt;(DUMMY_TOKENS[0]);
const [currency, setCurrency] = useState&lt;string&gt;(&quot;USD&quot;);
const [price, setPrice] = useState(0);
const [high_24h, setHigh_24h] = useState(0);
const [low_24h, setLow_24h] = useState(0);
const [volume_24h, setVolume_24h] = useState(0);
const [price_24h, setPrice_24h] = useState(0);
const { data, isError, isLoading, error } = useQuery(
[crypto],
Api.fetchTokens
);
useEffect(() =&gt; {
if (!isLoading &amp;&amp; !isError &amp;&amp; data) {
exchangeHandler(currency, selectedToken);
}
}, [currency, selectedToken, data, isError, isLoading]);
const exchangeHandler = (currency: string, token: ICrypto) =&gt; {
if (currency === &quot;USD&quot;) {
setPrice(+token.tokenStat.price * 1);
setPrice_24h(+token.tokenStat.price_24h * 1);
setHigh_24h(+token.tokenStat.high_24h * 1);
setLow_24h(+token.tokenStat.low_24h * 1);
setVolume_24h(+token.tokenStat.volume_24h * 1);
} else if (currency === &quot;EUR&quot;) {
setPrice(+token.tokenStat.price * 0.92);
setPrice_24h(+token.tokenStat.price_24h * 0.92);
setHigh_24h(+token.tokenStat.high_24h * 0.92);
setLow_24h(+token.tokenStat.low_24h * 0.92);
setVolume_24h(+token.tokenStat.volume_24h * 0.92);
} else if (currency === &quot;RSD&quot;) {
setPrice(+token.tokenStat.price * 107.66);
setPrice_24h(+token.tokenStat.price_24h * 107.66);
setHigh_24h(+token.tokenStat.high_24h * 107.66);
setLow_24h(+token.tokenStat.low_24h * 107.66);
setVolume_24h(+token.tokenStat.volume_24h * 107.66);
}
};
if (isLoading) return &lt;h2&gt;Loading...&lt;/h2&gt;;
if (isError)
return (
&lt;&gt;
&lt;h2&gt;Something went wrong...&lt;/h2&gt;
&lt;p&gt;{error.toString()}&lt;/p&gt;
&lt;/&gt;
);
const { t } = useTranslation(&quot;common&quot;);
const tokensSelectOptions = data.map((el) =&gt; {
return (
&lt;Select.Option key={el.id} value={JSON.stringify(el)} label={el.name}&gt;
{el.name}
&lt;/Select.Option&gt;
);
});
const currencySelectOptions = [
&lt;Select.Option
value=&quot;USD&quot;
label={`${selectedToken.symbol}/USD`}
key={`${selectedToken.symbol}/USD`}
&gt;
{`${selectedToken.symbol}/USD`}
&lt;/Select.Option&gt;,
&lt;Select.Option
value=&quot;EUR&quot;
label={`${selectedToken.symbol}/EUR`}
key={`${selectedToken.symbol}/EUR`}
&gt;
{`${selectedToken.symbol}/EUR`}
&lt;/Select.Option&gt;,
&lt;Select.Option
value=&quot;RSD&quot;
label={`${selectedToken.symbol}/RSD`}
key={`${selectedToken.symbol}/RSD`}
&gt;
{`${selectedToken.symbol}/RSD`}
&lt;/Select.Option&gt;,
];
const handleSelectedToken = (value: any) =&gt; {
const token = value &amp;&amp; JSON.parse(value.value);
setSelectedToken(token);
};
return (
&lt;div&gt;
&lt;Row className={styles.gutterRow}&gt;
&lt;Col md={19} sm={19} xs={15} className={styles.gutterCol}&gt;
&lt;div className={styles.select}&gt;
&lt;Image src={bitcoin} alt=&quot;bitcoin&quot; className={styles.image} /&gt;
&lt;Space wrap&gt;
&lt;Select
defaultValue={{
value: data[0].name,
label: data[0].name,
}}
labelInValue={true}
style={{ width: 100 }}
onChange={(value) =&gt; handleSelectedToken(value)}
&gt;
{tokensSelectOptions}
&lt;/Select&gt;
&lt;Select
style={{ width: 100 }}
onChange={(value) =&gt; setCurrency(value.value)}
defaultValue={{
value: &quot;USD&quot;,
label: `${selectedToken.symbol}/USD`,
}}
labelInValue={true}
&gt;
{currencySelectOptions}
&lt;/Select&gt;
&lt;/Space&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p className={styles.orangeP}&gt;
{numeral(price).format(numFormat)} {currency}
&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div className={styles.changes}&gt;
&lt;p className={styles.orangeP}&gt;
{numeral(price_24h).format(numFormat)}
&lt;/p&gt;
&lt;p className={styles.orangeP}&gt;
{numeral(selectedToken.tokenStat.change_24h).format(numFormat)}
&lt;/p&gt;
&lt;/div&gt;
&lt;h6&gt;24h {t(&quot;exchange.changes&quot;)}&lt;/h6&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;{numeral(high_24h).format(numFormat)}&lt;/p&gt;
&lt;h6&gt;24h {t(&quot;exchange.high&quot;)}&lt;/h6&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;{numeral(low_24h).format(numFormat)}&lt;/p&gt;
&lt;h6&gt;24h {t(&quot;exchange.low&quot;)} &lt;/h6&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;{numeral(volume_24h).format(numFormat)}&lt;/p&gt;
&lt;h6&gt;
24h {t(&quot;exchange.volume&quot;)}({selectedToken.symbol})
&lt;/h6&gt;
&lt;/div&gt;
&lt;/Col&gt;
&lt;Col md={1} sm={1} xs={2} offset={1}&gt;
&lt;button className={styles.btn}&gt;{t(&quot;exchange.buy&quot;)}&lt;/button&gt;
&lt;/Col&gt;
&lt;Col  md={2} sm={2} xs={5} offset={1}&gt;
&lt;button className={styles.btn}&gt;{t(&quot;exchange.add&quot;)}&lt;/button&gt;
&lt;/Col&gt;
&lt;/Row&gt;
&lt;/div&gt;
);
};
export default CryptoExchange;

And I am getting this error in console:
Warning: React has detected a change in the order of Hooks called by CryptoExchange. This will lead to bugs and errors if not fixed.

How can I solve that error?

Thank you in advance.

I tried to change the order of Hooks, but I couldn't solve the problem.

答案1

得分: 0

你的代码问题在于在声明所有钩子之前有条件地返回组件。

在React中,跨多次渲染组件时保持钩子调用的顺序和一致性非常重要。如果在声明所有钩子之前有条件地返回,可能会导致某些钩子在特定渲染期间不包含在组件的状态中,从而导致此错误。

要解决此问题,请确保所有钩子调用都放置在组件的顶层,并且在任何条件返回之前声明它们。

此外,请确保所有返回语句都放置在函数的末尾,而不是在组件中间隔着。

通过遵循这些准则,您可以避免错误,并确保所有钩子都正确地成为组件状态的一部分。

英文:

The issue in your code is that you are conditionally returning a component before declaring all the hooks.

In React, it's important to maintain the order and consistency of hook calls across multiple renders of a component. If you return conditionally before declaring all the hooks, some of them may not be included in the component's state during a certain render, resulting in this error.

To resolve this issue, make sure that all hook calls are placed at the top level of your component, and that you declare them before any conditional returns.

Additionally, ensure that all returns are placed at the end of the function, rather than interspersed throughout the component.

By following these guidelines, you can avoid the error and ensure that all hooks are correctly part of the component's state.

huangapple
  • 本文由 发表于 2023年7月17日 20:57:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704711.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定