• Suspense 컴포넌트는 지연 적재가 발생할 때, fallback 렌더링

    • pending
      • throw fallback ui
    • resolved
      • rerender child component render
    • rejected
      • throw error fallback 무시, ErorrBoundary로 전달
  • 프라미스 던지기로 suspense 실행

  • 클로저로 내부 변수 캡슐화, 참조

  •   import React, {Suspense} from "react";
      
      function createResource(fetcher) {
        let status = 'pending';
        let result: any;
      
        const suspender = fetcher.then(
          (res) => {
            status = 'success';
            result = res;
          },
          (err) => {
            status = 'error';
            result = err;
          }
        );
      
        return {
          read() {
            if (status === 'pending') throw suspender;
            if (status === 'error') throw result;
            return result;
          }
        };
      }
      
      const userResource = createResource( fetch('https://jsonplaceholder.typicode.com/users/1')
          .then(res => res.json())
      );
      
      function User() {
        const user = userResource.read(); // 동기처럼 읽기
        return <div>Username: {user.name}</div>;
      }
      
      function App() {
        return (
        <ErrornBoundary fallback={} >
          <Suspense fallback={}>
            <User />
          </Suspense>
      </ErrorBoundary>
        );
      }

Retry version

  •   import React, { useState, Suspense } from "react";
      
      // 1. createResource
      function createResource<T>(fetcher: Promise<T>) {
        let status = "pending";
        let result: T;
        let error: any;
      
        const suspender = fetcher.then(
          (res) => {
            status = "success";
            result = res;
          },
          (err) => {
            status = "error";
            error = err;
          }
        );
      
        return {
          read() {
            if (status === "pending") throw suspender;
            if (status === "error") throw error;
            return result;
          },
        };
      }
      
      // 2. ErrorBoundary
      class ErrorBoundary extends React.Component<
        { fallback?: React.ReactNode; onReset?: () => void },
        { hasError: boolean }
      > {
        constructor(props: any) {
          super(props);
          this.state = { hasError: false };
        }
      
        static getDerivedStateFromError() {
          return { hasError: true };
        }
      
        handleRetry = () => {
          this.setState({ hasError: false });
          this.props.onReset?.(); // 부모에 알림 → 리소스 갱신 트리거
        };
      
        render() {
          if (this.state.hasError) {
            return (
              <div>
                ❌ 에러 발생
                <button onClick={this.handleRetry}>🔁 다시 시도</button>
              </div>
            );
          }
      
          return this.props.children;
        }
      }
      
      // 3. User (Suspense에서 fetch read)
      function User({ resource }: { resource: ReturnType<typeof createResource> }) {
        const user = resource.read();
        return <div>👤 Username: {user.name}</div>;
      }
      
      // 4. App (리소스를 상태로 관리 → 재생성 가능)
      export default function App() {
        const createUserResource = () =>
          createResource(
            fetch("https://jsonplaceholder.typicode.com/users/1").then((res) => {
              if (!res.ok) throw new Error("유저 정보 가져오기 실패");
              return res.json();
            })
          );
      
        const [resource, setResource] = useState(createUserResource);
      
        return (
          <ErrorBoundary onReset={() => setResource(createUserResource())}>
            <Suspense fallback={<div>⏳ 로딩 중...</div>}>
              <User resource={resource} />
            </Suspense>
          </ErrorBoundary>
        );
      }