Jump to content
Search Community

useGSAP text error

aleks3650 test
Moderator Tag

Recommended Posts

hello, 

I have some issue with me react app. Im trying to create animation to my page and everything is working properly untill user choose wrong country, than app crash with error code: Rendered fewer hooks than expected. This may be caused by an accidental early return statement. Animation itself works properly. 

And additionaly i see warning: GSAP target undefined not found.

I would be greatful for answear :D


 

import { useEffect, useRef, useState } from "react";
import { gsap } from "gsap";
import { useGSAP } from "@gsap/react";
import { TextPlugin } from "gsap/TextPlugin";
 
function App() {
  gsap.registerPlugin(useGSAP, TextPlugin);
  const UsaFlag = "url('https://flagcdn.com/w320/us.png')";
  const [FirstCountry, setFirstCountry] = useState("");
  const [FirstCountryInfo, setFirstCountryInfo] = useState(null);
  const [SecondCountry, setSecondCountry] = useState("");
  const [SecondCountryInfo, setSecondCountryInfo] = useState(null);
  const [AnotherCountry, setAnotherCountry] = useState("");
  const [AnotherCountryInfo, setAnotherCountryInfo] = useState(null);
  const [error, setError] = useState(null);
  const [choosed, setChoosed] = useState(false);
  const [gameOver, setGameOver] = useState(false);
  const [newSize, setNewSize] = useState("");
 
  const lowerref = useRef();
  const upperref = useRef();
 
  async function fetchData() {
    try {
      const response = await fetch("https://restcountries.com/v3.1/all");
      const data = await response.json();
      const randomIndex = Math.floor(Math.random() * data.length);
      const randomCountry = data[randomIndex];
      const name = randomCountry.name.common;
      setFirstCountry(name);
      const secondRandomIndex = Math.floor(Math.random() * data.length);
      const secondRandomCountry = data[secondRandomIndex];
      const secondName = secondRandomCountry.name.common;
      setSecondCountry(secondName);
    } catch (error) {
      setError("Failed to fetch data. Please try again later.");
    }
  }
 
  async function fetchSingleData(chosenCountry, index) {
    try {
      const response = await fetch(
        `https://restcountries.com/v3.1/name/${chosenCountry}`
      );
      const data = await response.json();
      if (data && data.length > 0) {
        if (index === 1) setFirstCountryInfo(data[0]);
        if (index === 2) {
          setSecondCountryInfo(data[0]);
          setNewSize(`${NameFun(data[0].area.toString())} km²`);
        }
      } else {
        setError(`No data found for ${chosenCountry}.`);
      }
    } catch (error) {
      setError(`Failed to fetch data for ${chosenCountry}.`);
    }
  }
 
  async function fetchAnotherDataInfo() {
    const response = await fetch("https://restcountries.com/v3.1/all");
    const data = await response.json();
    const randomIndex = Math.floor(Math.random() * data.length);
    const randomCountry = data[randomIndex];
    const name = randomCountry.name.common;
    setAnotherCountry(name);
    try {
      const response = await fetch(
        `https://restcountries.com/v3.1/name/${name}`
      );
      const data = await response.json();
      if (data && data.length > 0) {
        setAnotherCountryInfo(data[0]);
      } else {
        setError(`No data found for ${name}.`);
      }
    } catch (error) {
      setError(`Failed to fetch data for ${name}.`);
    }
  }
 
  useEffect(() => {
    fetchData();
  }, []);
 
  useEffect(() => {
    if (FirstCountry) {
      fetchSingleData(FirstCountry, 1);
    }
    if (SecondCountry) {
      fetchSingleData(SecondCountry, 2);
    }
  }, [FirstCountry, SecondCountry]);
 
  useEffect(() => {
    fetchAnotherDataInfo();
  }, [FirstCountry, SecondCountry]);
 
  if (error) {
    return <div className="errorr">Error: {error}</div>;
  }
 
  const handleNextOne = () => {
    setFirstCountry(SecondCountry);
    setSecondCountryInfo(SecondCountryInfo);
    // setNewSize(SecondCountryInfo.area.toString());
    setSecondCountry(AnotherCountry);
    setAnotherCountryInfo(AnotherCountryInfo);
    setChoosed(false);
  };
 
  const handleTryAgain = () => {
    setGameOver(false);
    setChoosed(false);
    setError(false);
    setFirstCountry(false);
    setFirstCountryInfo(false);
    setSecondCountry(false);
    setSecondCountryInfo(false);
    fetchData();
  };
 
  const handleGameOver = () => {
    setGameOver(true);
  };
 
  const handleCompare = (value) => {
    setChoosed(true);
    fetchAnotherDataInfo();
    setTimeout(() => {
      if (
        (FirstCountryInfo.area <= SecondCountryInfo.area && value === "more") ||
        (FirstCountryInfo.area >= SecondCountryInfo.area && value === "less")
      ) {
        handleNextOne();
      } else {
        handleGameOver();
      }
    }, "1000");
  };
 
  const NameFun = (area) => {
    let numStr = area.toString();
    if (numStr.includes(".")) {
      return area;
    }
    let newtab = [];
    for (let i = numStr.length; i > 0; i -= 3) {
      let chunk = numStr.slice(Math.max(0, i - 3), i);
      newtab.unshift(chunk);
    }
    let spacedNum = newtab.join(" ");
    return spacedNum;
  };
  const styleh1 = {
    letterSpacing: "10px",
  };
  if (gameOver) {
    return (
      <div className="GameOverScreen">
        <h1 style={styleh1}>GAME OVER</h1>
        <p>You can do better</p>
        <button className="more tryAgain" onClick={() => handleTryAgain()}>
          TRY AGAIN
        </button>
      </div>
    );
  }
 
  useGSAP(
    () => {
      gsap.to(lowerref.current, {
        duration: 0.9,
        delay: 0.1,
        text: newSize,
        ease: "bounce.inOut",
      });
    },
    { dependencies: [AnotherCountryInfo] }
  );
 
  return (
    <>
      <div className="app">
        <h1>More or less</h1>
        <div className="container">
          <div className="versus">VS</div>
          <div
            className="country first"
            style={{
              backgroundImage: FirstCountryInfo
                ? `linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url(${
                    FirstCountryInfo.flags ? FirstCountryInfo.flags.png : ""
                  })`
                : "",
            }}>
            <h2>
              {FirstCountryInfo
                ? FirstCountryInfo.name.common || "Country"
                : "Loading..."}
            </h2>
            <p>is</p>
            <h3>
              <span>
                {FirstCountryInfo
                  ? `${NameFun(FirstCountryInfo.area) || "Area"} km²`
                  : "Loading..."}
              </span>
            </h3>
            <p>in total area</p>
          </div>
          <div
            className="country second"
            ref={upperref}
            style={{
              backgroundImage: SecondCountryInfo
                ? `linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url(${
                    SecondCountryInfo.flags ? SecondCountryInfo.flags.png : ""
                  })`
                : "",
            }}>
            <h2>
              {SecondCountryInfo
                ? `${SecondCountryInfo.name.common || "Country"}`
                : "Loading..."}
            </h2>
            <p>is</p>
            {choosed ? (
              <h3>
                <span className="AreaInfo" ref={lowerref}>
                  XXXXXXXXXXX km²
                </span>
              </h3>
            ) : (
              // {NameFun(newSize) || "Area"}
              <div className="buttonContainer">
                <button className="less" onClick={() => handleCompare("less")}>
                  smaller
                </button>
                <button className="more" onClick={() => handleCompare("more")}>
                  bigger
                </button>
              </div>
            )}
            <p>in Total Area</p>
          </div>
 
          <div
            className="country third"
            style={{ backgroundImage: UsaFlag }}></div>
        </div>
      </div>
    </>
  );
}
 
export default App;

App.jsx

Link to comment
Share on other sites

Hi @aleks3650 and welcome to the GSAP Forums!

 

There is far too much code in here and without a minimal demo there is not a lot we can do.

 

The first error you're getting is mostly a React related one and not a GSAP related one:

https://dev.to/collegewap/fix-rendered-fewer-hooks-than-expected-in-react-3757

https://medium.com/@jonchurch/how-to-fix-react-error-rendered-fewer-hooks-than-expected-e6a378985d3c

 

The issue with the target undefined, that means that the element you're passing to GSAP is undefined most likely because of that conditional rendering block you  have here:

{choosed ? (
<h3>
  <span className="AreaInfo" ref={lowerref}>
    XXXXXXXXXXX km²
  </span>
</h3>
) : (
// {NameFun(newSize) || "Area"}
<div className="buttonContainer">
  <button className="less" onClick={() => handleCompare("less")}>
    smaller
  </button>
  <button className="more" onClick={() => handleCompare("more")}>
    bigger
  </button>
</div>
)}

When choosed is falsy lowerref is no longer a DOM element but is undefined:

useGSAP(
  () => {
    gsap.to(lowerref.current, { // UNDEFINED -> WARNING!
      duration: 0.9,
      delay: 0.1,
      text: newSize,
      ease: "bounce.inOut",
    });
  },
  { dependencies: [AnotherCountryInfo] }
);

I would suggest you to use revertOnUpdate in the useGSAP hook, but I'm afraid that it won't fix the logic issue of the ref not being defined. You should look into that first.

 

We have a collection of starter templates in Stackblitz where you can fork one of those and create a minimal demo:

https://stackblitz.com/@gsap-dev/collections/gsap-react-starters

 

Happy Tweening!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...