Skip to content

Commit 2fcea59

Browse files
JoviDeCroockkitten
authored andcommitted
support concurrent prepasses (#37)
* support concurrent prepasses * remove unused import * reset hook state before initialization
1 parent e82ad0c commit 2fcea59

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

src/__tests__/concurrency.test.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React, {
2+
createContext,
3+
useContext,
4+
useState,
5+
useMemo,
6+
useRef,
7+
useCallback
8+
} from 'react'
9+
import renderPrepass from '..'
10+
11+
const CONCURRENCY = 2
12+
13+
const Context = createContext({ test: 3, promise: null, resolved: false })
14+
15+
function makeApp() {
16+
return <App />
17+
}
18+
19+
function App() {
20+
const [state, setState] = useState(() => ({
21+
test: Math.random(),
22+
promise: null,
23+
resolved: false
24+
}))
25+
const refresh = () =>
26+
setState({ test: Math.random(), promise: null, resolved: false })
27+
28+
return (
29+
<Context.Provider value={{ ...state, refresh }}>
30+
<Outer />
31+
</Context.Provider>
32+
)
33+
}
34+
35+
function Outer() {
36+
useRef({
37+
test: 1
38+
})
39+
40+
const [, refresh] = useSuspenseHook()
41+
42+
useMemo(() => {
43+
return { a: 1, b: 2 }
44+
}, [])
45+
46+
return (
47+
<div>
48+
<button onClick={refresh}>Refresh</button>
49+
<Inner />
50+
</div>
51+
)
52+
}
53+
54+
function useSuspenseHook() {
55+
const context = useContext(Context)
56+
57+
useRef({
58+
test: 1
59+
})
60+
61+
if (!context.resolved && !context.promise) {
62+
context.promise = new Promise(resolve =>
63+
setTimeout(resolve, Math.floor(30 + Math.random() * 50))
64+
).then(() => {
65+
context.resolved = true
66+
context.promise = null
67+
})
68+
}
69+
70+
if (context.promise) throw context.promise
71+
72+
return [true, context.refresh]
73+
}
74+
75+
function Inner() {
76+
const [state] = useState({ a: 3 })
77+
78+
useCallback(() => {
79+
return state
80+
}, [state])
81+
82+
return <h4>Inner</h4>
83+
}
84+
85+
test('concurrency', () => {
86+
return expect(
87+
Promise.all(
88+
new Array(CONCURRENCY).fill(0).map(() => renderPrepass(makeApp()))
89+
)
90+
).resolves.not.toThrow()
91+
})

src/internals/dispatcher.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export function renderWithHooks(
113113
props: any,
114114
refOrContext: any
115115
): any {
116+
workInProgressHook = null
116117
let children = Component(props, refOrContext)
117118

118119
// NOTE: Excessive rerenders won't throw but will instead abort rendering

0 commit comments

Comments
 (0)