[React v18] Suspense

김태완·2023년 6월 27일
0

React

목록 보기
22/24

React v18의 Suspense 기능에 대해 알아보자

  • Suspense : 미결,미결정

Suspense 적용전

  • useState(data, isLoading)과 useEffect에서 data fetch를 활용
  • 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>
    )
}

Suspense 적용후

  • react-query의 useQuery와 함께사용 (suspense옵션)
  • ChildComponentGrandChildComponent의 fetch가 종료된 후 화면이 보여진다
  • fallback이 적용이 안된듯..
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>
        </>
    )
}
  • 기존의 방식은 layout shift가 일어나, fetch에 오랜 시간이 걸린다면 사용자 경험에 치명적이다.
  • layout shift가 일어나지않는 Suspense를 사용하는것이 사용자 경험 향상에 좋을거같다
  • 추가로 SuspenseSkeleton UI까지 사용하면 금상첨화일듯

lazy + Suspense

  • react 18버전의 Suspense를 Routes위에 감싸주면 code split된 페이지가 로드될때까지 fallback을 보여준다.
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>
	

react-query + Suspense

  • Suspense로 감싸준 컴포넌트가 fetching 작업일때 각각의 fallback을 보여줌
// 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>;
};

pending

fetched

profile
프론트엔드개발

0개의 댓글