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