@@ -2,9 +2,46 @@ import React from 'react'
2
2
import { useStyleRegistry , createStyleRegistry } from './stylesheet-registry'
3
3
import { computeId } from './lib/hash'
4
4
5
- // Opt-into the new `useInsertionEffect` API in React 18, fallback to `useLayoutEffect`.
6
- // https://github.com/reactwg/react-18/discussions/110
7
- const useInsertionEffect = React . useInsertionEffect || React . useLayoutEffect
5
+ function useRegistry ( registry , props ) {
6
+ const addToRegistry = React . useCallback ( ( ) => {
7
+ registry . add ( props )
8
+ return ( ) => {
9
+ registry . remove ( props )
10
+ }
11
+ // props.children can be string[], will be striped since id is identical
12
+ } , [ props . id , String ( props . dynamic ) ] )
13
+
14
+ const addedDuringInsertionEffectRef = React . useRef ( false )
15
+
16
+ // Opt-into the new `useInsertionEffect` API in React 18.
17
+ // https://github.com/reactwg/react-18/discussions/110
18
+ if ( React . useInsertionEffect ) {
19
+ React . useInsertionEffect ( ( ) => {
20
+ // `useInsertionEffect` can be called while `document.head` is null
21
+ // (e.g. when `hydrateRoot` is passed `document` as the first argument and there
22
+ // is a hydration mismatch). This causes `registry.add` to throw an error,
23
+ // preventing React from recovering during client rendering after the mismatch
24
+ try {
25
+ const removeFromRegistry = addToRegistry ( )
26
+ addedDuringInsertionEffectRef . current = true
27
+ return removeFromRegistry
28
+ } catch ( _ ) { }
29
+ } , [ addToRegistry ] )
30
+ }
31
+
32
+ // Use `useLayoutEffect` as a fallback for React 16/17 and in case `useInsertionEffect`
33
+ // fails
34
+ React . useLayoutEffect ( ( ) => {
35
+ if ( addedDuringInsertionEffectRef . current ) {
36
+ // `useLayoutEffect` is called after `useInsertionEffect`.
37
+ // Reset the ref in case a future failure in `useInsertionEffect` occurs
38
+ addedDuringInsertionEffectRef . current = false
39
+ return
40
+ }
41
+
42
+ return addToRegistry ( )
43
+ } , [ addToRegistry ] )
44
+ }
8
45
9
46
const defaultRegistry =
10
47
typeof window !== 'undefined' ? createStyleRegistry ( ) : undefined
@@ -21,13 +58,7 @@ export default function JSXStyle(props) {
21
58
return null
22
59
}
23
60
24
- useInsertionEffect ( ( ) => {
25
- registry . add ( props )
26
- return ( ) => {
27
- registry . remove ( props )
28
- }
29
- // props.children can be string[], will be striped since id is identical
30
- } , [ props . id , String ( props . dynamic ) ] )
61
+ useRegistry ( registry , props )
31
62
32
63
return null
33
64
}
0 commit comments