
// This is a React Quiz from BFE.dev
import * as React from 'react'
import { Suspense } from 'react'
import { createRoot } from 'react-dom/client'
const resource = (() => {
let data = null
let status = 'pending'
let fetcher = null
return {
get() {
if (status === 'ready') {
return data
}
if (status === 'pending') {
fetcher = new Promise((resolve, reject) => {
setTimeout(() => {
data = 1
status = 'ready'
resolve()
}, 100)
})
status = 'fetching'
}
throw fetcher
}
}
})()
function A() {
console.log('A1')
const data = resource.get()
console.log('A2')
return <p>{data}</p>
}
function B() {
console.log('B')
return null
}
function Fallback() {
console.log('fallback')
return null
}
function App() {
console.log('App')
return <div>
<Suspense fallback={<Fallback/>}>
<A/>
<B/>
</Suspense>
</div>
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
"App"
"A1"
"B"
"fallback"
"A1"
"A2"
"B"
App 출력: root.render(<App/>)에 의해 App 컴포넌트가 호출됩니다.
A1 출력: App 내부의 A가 실행됩니다.
중단(Suspend) : 컴포넌트 A는 throw에 의해 실행이 중단됩니다.B 출력: App 내부의 B가 실행됩니다.
Suspense가 에러(Promise)를 캐치: React는 위에서 던져진 Promise를 감지합니다.
fallback 출력: Promise가 처리되는 동안 보여줄 Fallback 컴포넌트를 렌더링합니다.
✨ React는
Promise가 해결되었으므로 중단되었던 부분부터 다시 그리려 합니다.
React는 효율성을 위해App전체를 다시 그리는 것이 아니라, Suspense 경계 내부인<A/><B/>만 다시 실행합니다.
그래서App로그가 다시 찍히지 않는 것입니다.
A1 출력: A 컴포넌트가 처음부터 다시 실행됩니다.
A2 출력: 데이터를 반환하고 A2가 출력됩니다.
B 출력: 나중에 데이터가 준비되어 다시 그리기로 결정하면, Suspense 내부에 있는 모든 자식 컴포넌트(A와 B 전체)를 처음부터 다시 렌더링합니다.