React v18의 Suspense 기능에 대해 알아보자
ChildComponent
의 fetch가 끝나고 GrandChildComponent
의 fetch가 시작된다. 따라서 layout shift
가 일어날 확률이 높음import React, {useState,useEffect} from 'react'
export default function ParentComponent() {
return <ChildComponent />
}
const ChildComponent = () => {
const [list, setList] = useState([])
const [isLoading, setIsLoading] = useState(false)
const getListPromise = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/albums");
const json = await res.json();
return json
}
useEffect(() => {
setIsLoading(true)
getListPromise().then(res => {
setList(res)
setIsLoading(false)
})
}, []);
if(isLoading) return <div style={{color:"#f00"}}>자식요소 로딩중...</div>
return (
<div>
<h2>자식요소</h2>
<div>{list.slice(0,10).map((l, i) => <div key={i}>{l.title}</div>)}</div>
<GrandChildComponent />
</div>
)
}
const GrandChildComponent = () => {
const [list, setList] = useState([])
const [isLoading, setIsLoading] = useState(false)
const getListPromise = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const json = await res.json();
return json
}
useEffect(() => {
setIsLoading(true)
getListPromise().then(res => {
setList(res)
setIsLoading(false)
})
}, []);
if(isLoading) return <div style={{color:"#f00"}}>자식의 자식 요소 로딩중...</div>
return (
<div>
<h2>자식의 자식 요소</h2>
<div>{list.slice(0,10).map((l, i) => <div key={i}>{l.title}</div>)}</div>
</div>
)
}
ChildComponent
와 GrandChildComponent
의 fetch가 종료된 후 화면이 보여진다import React, {Suspense} from 'react'
import { useQuery } from "@tanstack/react-query"
export default function ParentComponent() {
return (
<Suspense fallback={<div style={{color:"#f00"}}>로딩중...</div>}>
<Child />
<GrandChild />
</Suspense>
)
}
const Child = () => {
const getListPromise = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const json = await res.json();
return json
}
const { data, isLoading} = useQuery(["child"], () => getListPromise(), { suspense:true});
return (
<>
<h2>자식요소</h2>
<div>{data?.slice(0,10).map((l, i) => <div key={i}>{l.title}</div>)}</div>
</>
)
}
const GrandChild = () => {
const getListPromise = async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/albums");
const json = await res.json();
return json
}
const { data, isLoading} = useQuery(["child"], () => getListPromise(),{ suspense:true});
return (
<>
<h2>자식의 자식 요소</h2>
<div>{data?.slice(0,10).map((l, i) => <div key={i}>{l.title}</div>)}</div>
</>
)
}
Suspense
에 Skeleton UI
까지 사용하면 금상첨화일듯import { lazy, Suspense } from "react";
// router/index.jsx
const WorkerList = lazy(() => import("../home/page/worker/list"));
const WorkerView = lazy(() => import("../home/page/worker/view"));
const WorkerCreate = lazy(() => import("../home/page/worker/create"));
<Suspense fallback={<SplashScreen />}>
<Routes>
<Route path="/index.html" element={<Navigate to={"/"} />} />
<Route path="/" element={<Home />} />
<Route path="/samplepage" element={<Samplepage />} />
<Route path="/login" element={<Logoin />} />
{/* 근로자 */}
<Route path="/worker" element={<Navigate to={"/worker/list"} />} />
<Route path="/worker/list" element={<WorkerList />} />
<Route path="/worker/view" element={<WorkerView />} />
<Route path="/worker/regist" element={<WorkerCreate />} />
</Routes>
</Suspense>
// parent.jsx
return (
<>
<Suspense fallback={<div>인포 로딩중..</div>}>
<Child01 />
</Suspense>
<Suspense fallback={<div>디테일 로딩중..</div>}>
<Child02 />
</Suspense>
</>
)
// childs
const Child01 = () => {
const { data } = useQuery(
["info"],
() => getWorkerInfo({ ...req, tabId: "A" }),
{
suspense: true,
}
);
return <div>인포 : {data.data.siteWorkerRiskDetail.weekMinRiskValue}</div>;
};
const Child02 = () => {
const { data } = useQuery(["detail"], () => getWorkerDetail(req), {
suspense: true,
});
return <div>디테일 : {data.data.workerName}</div>;
};