React Suspense I
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 Fallback() {
console.log('fallback')
return null
}
function App() {
console.log('App')
return <div>
<Suspense fallback={<Fallback/>}>
<A/>
</Suspense>
</div>
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
console:“App” “A1” “fallback” “A1” “A2”
A 开始渲染,但随后 resources.get() 抛出待处理的 Promise,此时激活了Suspense组件,转向了 Suspense 并渲染其后备组件。
React 文档的描述 - “只有支持 Suspense 的数据源才会激活 Suspense 组件”。
在 Promise resolve后,A重新渲染
React Suspense II
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/>)
console:“App” “A1” “B” “fallback” “A1” “A2” “B”
- Suspense“检查”所有组件,即每个组件的主体代码都会被执行。
- 首先渲染 APP组件记录“App”
- 接下来,在 Suspense 中渲染组件:第一个组件A 记录A1,第二个组件 B记录B
- React 将显示自定义加载fallback,直到加载完子级所需的所有代码和数据
- 接下来,在所有加载结束后,Suspense 卸载后备组件并再次渲染子组件记录"A1" “A2” “B”
React Suspense III
import * as React from 'react'
import { Suspense, useMemo } from 'react'
import { createRoot } from 'react-dom'
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(1)
const memoed = useMemo(() => {
console.log(2)
return 'memo'
}, [])
const data = resource.get()
console.log(3)
return memoed + data
}
function App() {
return <Suspense fallback={null}>
<A/>
</Suspense>
}
const root = createRoot(document.getElementById('root'));
root.render(<App/>)
console:
“1”
“2”
“1”
“2” // 组件里useMemo函数会再次调用,并再次将 2 记录到控制台
“3”