반응형

※ React 18 버전에서의 Suspense

  - 각 페이지에 구성되 있는 컴포넌트들을 동시에 호출하는 것이 아닌 영역별로 렌더링 시점을 동기화 처리
  - 이전 버전까지는 클라이언트 컴포넌트에서만 제한적으로 동작되는 기술이었으나 18버전부터는 ssr 방식의 컴포넌트에서도 활용 가능하도록 개선

활용 예제)
  1. 특정 컴포넌트가 렌더링 완료 될 때까지 다른 컴포넌트의 렌더링을 막고 이전 렌더링 컴포넌트 완료 후 동기적으로 렌더링 시작
  2. 서버로부터 무거운 데이터를 fecthing하는 컴포넌트의 경우 해당 컴포넌트 출력전까지 자동으로 로딩바 출력

 

 

suspense를 활용 하기 위한 조건

- suspense 동기화 시키는 컴포넌트 내부에 promise 객체 생성 상태(pending, fullfilled , rejected)를 추적 할 수 있어야 됨.

 

 

예제)  useGetData.js

import { useState, useEffect } from 'react';
import axios from 'axios';

//promise 객체를 인수로 받아서 해당 promise 상태에 따라 반환되는 값을 직접 리턴해주는 함수를 반환
const checkPromiseStatus = (promise) => {
	let status = 'pending';
	let result;

	//promise의 상태에 따라 현재 상태값과 반환값을 각각 status, result 변수에 담아줌
	const setPromise = promise.then(
		(value) => {
			status = 'success';
			result = value;
		},
		(error) => {
			status = 'error';
			result = error;
		}
	);

	//위에서 저장되는 status값에 따라 fetching된 결과값을 반환하는 함수를 리턴
	return () => {
		switch (status) {
			case 'pending':
				throw setPromise;
			case 'success':
				return result;
			case 'error':
				throw result;
			default:
				throw new Error('Unknow Status');
		}
	};
};

function useGetData(url) {
	const [Data, setData] = useState(null);

	useEffect(() => {
		const getData = async () => {
			//데이터 요청 후 현재 데이터 상태를 확인하는 promise 객체 자체를 비동적으로 받음
			const promise = axios.get('https://jsonplaceholder.typicode.com/posts').then((response) => response.data);

			//해당 promise 객체를 checkPromiseStatus 함수의 인수로 전달해서 직접 동기화 시키는 커스텀함수 호출후 결과값을 반환값을 state에 담아줌
			setData(checkPromiseStatus(promise));
		};

		getData();
	}, [url]);

	//state에 담아진 promise 반환 값을 리턴
	return Data;
}

export default useGetData;

 

Posts.js

import useGetData from './useGetData';

function Posts() {
	const data = useGetData('https://jsonplaceholder.typicode.com/posts');
	console.log(data);
	return (
		<div>
			{data &&
				data.map((post) => {
					return (
						<div key={post.id}>
							<h2>{post.title}</h2>
							<br />
							<p>{post.body}</p>
							<hr />
						</div>
					);
				})}
		</div>
	);
}

export default Posts;

 

App.jsx

import Posts from './Posts';
import { Suspense } from 'react';

function App() {
	return (
		<div className='App'>
			<h1>App</h1>
			<Suspense fallback={<p>Posts loadding...</p>}>
				<h1>Posts</h1>
				<Posts />
			</Suspense>
		</div>
	);
}

export default App;

+ Recent posts