<div id="root"></div>
body{
  margin: 0;
  padding: 0;
  height: 100vh;
}

.app {
  padding: 10px;
}

.box {
  margin: 10px 0;
}

.loading {
  font-size: 1.25rem;
}
const { useEffect, useRef, useState, useLayoutEffect } = React

// 模拟数据加载
function fetchFakeData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, color: "blue" },
        { id: 2, color: "red" },
        { id: 3, color: "purple" }
      ]);
    }, 2000);
  });
}

function Box({ children, color }) {
  return <div className={`box ${color}`}>{children}</div>;
}

function App() {
  const app = useRef();

  const [data, setData] = useState([]);
  const [loadingState, setLoadingState] = useState();

  useEffect(() => {
    if (loadingState !== "start") return;

    const loadData = async () => {
      const data = await fetchFakeData();
      setData(data);
      setLoadingState("complete");
    };
    loadData();
  }, [loadingState]);

  useLayoutEffect(() => {
    if (loadingState !== "complete") return;
    const ctx = gsap.context(() => {
      gsap.fromTo(
        ".box",
        {
          opacity: 0
        },
        {
          opacity: 1,
          duration: 1,
          stagger: 0.2
        }
      );
    }, app);
    return () => ctx.revert();
  }, [loadingState]);

  const startLoading = () => {
    if (!loadingState) {
      setLoadingState("start");
    }
  };

  return (
    <div className="panel flex-row" ref={app}>
      {!loadingState ? (
        <div>
          <button onClick={startLoading}>Start Loading</button>
        </div>
      ) : null}
      {loadingState === "start" ? (
        <div className="loading">Loading fake data...</div>
      ) : null}
      {data.map((item) => (
        <Box key={item.id} {...item}>
          Box {item.id}
        </Box>
      ))}
    </div>
  );
}

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.querySelector("#root")
);
View Compiled
Run Pen

External CSS

  1. https://codepen.io/GreenSock/pen/gOWxmWG.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js
  3. https://unpkg.co/gsap@3/dist/gsap.min.js