0

I would like to animate some text in React, a fade-out-and-in to a text object based on a change to values in a store. I'm thinking something like a useEffect with a dependency on said store would be able to solve this, but I'm not sure how to go about implementing it.

The values that I want to animate are the {selection.price.toFixed(2)} in the code below. Whenever markets changes, I would like the new text to fade out and in- or even highlight for a brief period during the state change:

export const MatchPrices = () => {
  const markets = useStore($markets);

  const updateNotificationRef = useRef();

  useEffect(() => {
 
    if (markets.length === 0) {
      return;
    }

    updateNotificationRef.current.animate(
      {
        opacity: [0, 1, 0],
      },
      1000
    );
  }, [markets]);

  return (
    <Card className="price-display">
      <Table className="card-table prices-table" size="sm">
        <thead className="card-header">
          <tr>
            <th>Home</th>
            <th>Draw</th>
            <th>Away</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            {markets.length !== 0 ? (
              markets
                .filter((market) => market.mt === "MATCH_WINNER")[0]
                .selections.map((selection) => (
                  <td
                    className="match-price"
                    key={selection.id}
                    ref={updateNotificationRef}
                    style={{ opacity: 1 }}
                  >
                    {selection.price.toFixed(2)}
                  </td>
                ))
            ) : (
              <td>
                <Spinner
                  animation="border"
                  role="status"
                  style={{
                    marginLeft: "Auto",
                    marginRight: "Auto",
                  }}
                ></Spinner>
              </td>
            )}
          </tr>
        </tbody>
      </Table>
    </Card>
  );
};

I'm not really sure how to get the CSS to work though.

clattenburg cake
  • 1,096
  • 3
  • 19
  • 40

1 Answers1

2

An example using web animations:

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

export default function App() {
  const initialState = 0;
  const [count, setCount] = useState(initialState);
  const updateNotificationRef = useRef();

  useEffect(() => {
    // Skipping the initial render. TODO: use a better solution from https://stackoverflow.com/questions/53179075/with-useeffect-how-can-i-skip-applying-an-effect-upon-the-initial-render
    if (count === initialState) {
      return;
    }
    updateNotificationRef.current.animate(
      {
        opacity: [0, 1, 0]
      },
      500
    );
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount((c) => c + 1)} children="Increment" />
      <div ref={updateNotificationRef} style={{ opacity: 0 }}>
        Updated
      </div>
    </div>
  );
}

Live demo: codesandbox.io

  • Thanks @Oleksandr, this is really helpful. When I use your example, only the third element of `{selection.price.toFixed(2)}` seems to update, and not the other two. Do you have any ideas why this might be the case? Code updated with your suggestion. – clattenburg cake Jul 22 '21 at 18:06
  • Don't worry that's my fault! I should put the effect on the row. Thanks so much. – clattenburg cake Jul 22 '21 at 18:13