I want to remove a className after a few seconds, or re trigger the class animation when condition is met

Viewed 770

Goal: When there is a price change,i want the price number to higlight for a few seconds with a color. I do that with the toogleClassName.

Problem: When the iteration is UP UP,or DOWN DOWN,the element already has that class and the CSS animation already ran.

JSX Code:

import React, { useEffect, useState, useRef } from "react";
import styles from "./CoinContainer.module.css";

function usePrevious(data) {
  const ref = useRef();
  useEffect(() => {
    ref.current = data;
  }, [data]);
  return ref.current;
}
export default function CoinContainer({ coin, price }) {
  const [priceUpdated, setPrice] = useState("");

  const prevPrice = usePrevious(priceUpdated);

  // Update price
  useEffect(() => {
    setInterval(priceUpdate, 20000);
  }, []);

  // Apply different flash-color style according to up$ or down$ from prev
  const toggleClassName = () => {
    return prevPrice > priceUpdated ? styles.redPrice : styles.greenPrice;
  };

  function priceUpdate() {
    return fetch(
      `https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
    )
      .then((data) => data.json())
      .then((result) => {
        let key = Object.keys(result);
        setPrice(result[key].usd);
      });
  }
  return (
    <div className={styles.padding}>
      <h2>{coin}</h2>
      {/* Here is the problem,i would like to remove the class after a few seconds,or edit the CSS code to retrigger the animation */}
      <h3 className={toggleClassName()}>
        {priceUpdated ? priceUpdated : price}$
      </h3>
    </div>
  );
}

CSS Code

@keyframes upFadeBetween {
  from {
    color: green;
  }
  to {
    color: white;
  }
}

@keyframes downFadeBetween {
  from {
    color: red;
  }
  to {
    color: white;
  }
}

.redPrice {
  animation: downFadeBetween 5s;
  color: white;
}

.greenPrice {
  animation: upFadeBetween 5s;
  color: white;
}

Thanks so much for any feedback/help!

3 Answers

You could use another variable called e.g. reset

const [reset, setReset] = useState(false);

And then set this value at your methods using setTimeout

  const toggleClassName = () => {
    setTimeout(() => setReset(true), 6000);
    return prevPrice > priceUpdated ? styles.redPrice : styles.greenPrice;
  };


  function priceUpdate() {
    return fetch(
      `https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
    )
      .then((data) => data.json())
      .then((result) => {
        let key = Object.keys(result);
        setReset(false);
        setPrice(result[key].usd);
      });
  }

and finally

<h3 className={reset ? "" : toggleClassName()}>

you can do this

handleClick = event => event.target.classList.add('click-state');

or as react is js library so definitly it will support js DOM elements, well there is no doubt that it uses DOM also. so you can do this also,

for adding class

document.getElementById("id here").classList.add("classname here");

for removing class

document.getElementById("id here").classList.remove("classname here");

OR

you can also use react States

You can use a variable that will be updated every time you trigger the fetch. In this case, we will call it animationStyle

function CoinContainer({ coin, price }) {
   const [priceUpdated, setPrice] = useState("")
   const [animationStyle, setAnimationStyle] = useState("")

   useEffect(() => {
      setInterval(priceUpdate, 20000)
   }, [])

   function updateAnimationStyle(){
      if (prevPrice > priceUpdated) {
         setAnimationStyle("redPrice")
      } else {
         setAnimationStyle("greenPrice")
      }
      setTimeout(() => {
         setAnimation("")
      }, 5000)
   }

   function priceUpdate() {
      return fetch(
         `https://api.coingecko.com/api/v3/simple/price?ids=${coin}&vs_currencies=usd`
      )
         .then((data) => data.json())
         .then((result) => {
            let key = Object.keys(result)
            setPrice(result[key].usd)
            updateAnimationStyle()
         })
   }
   return (
      <div className={styles.padding}>
         <h2>{coin}</h2>         
         <h3 className={animationStyle}>//you avoid doing the conditional rendering that might be a bit confusing
            {priceUpdated ? priceUpdated : price}$
         </h3>
      </div>
   )
}
  1. Okay, so basically, the approach is to avoid too large conditional rendering inside the jsx.
  2. the function updateAnimationStyle gets called every time the fetch is triggered.
  3. depending on the condition, updateAnimationStyle will set redPrice or greenPrice animations to animationStyle.
  4. the animationStyle will clean after 5 seconds through setTimeout function. "it can not be less than 5s because you have set the animation duration to 5s".
  5. const [priceUpdated, setPrice] = useState("") it is better to avoid this type of naming for variables.
  6. Instead, if you use const [priceUpdated, setPriceUpdated] = useState(""), it will be more readable.

Hope it works for you! Regards.

Related