@@ -168,11 +168,7 @@ function useModernSSRSafeId(defaultId?: string): string {
168
168
export const useSSRSafeId : typeof useModernSSRSafeId | typeof useLegacySSRSafeId = typeof React [ 'useId' ] === 'function' ? useModernSSRSafeId : useLegacySSRSafeId ;
169
169
170
170
function getSnapshot ( ) {
171
- return false ;
172
- }
173
-
174
- function getServerSnapshot ( ) {
175
- return true ;
171
+ return null ;
176
172
}
177
173
178
174
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -181,17 +177,41 @@ function subscribe(onStoreChange: () => void): () => void {
181
177
return ( ) => { } ;
182
178
}
183
179
184
- /**
185
- * Returns whether the component is currently being server side rendered or
186
- * hydrated on the client. Can be used to delay browser-specific rendering
187
- * until after hydration.
188
- */
189
- export function useIsSSR ( ) : boolean {
180
+ function useModernIsSSR ( ) : boolean {
181
+ let initialState = false ;
190
182
// In React 18, we can use useSyncExternalStore to detect if we're server rendering or hydrating.
191
- if ( typeof React [ 'useSyncExternalStore' ] === 'function' ) {
192
- return React [ 'useSyncExternalStore' ] ( subscribe , getSnapshot , getServerSnapshot ) ;
183
+ React . useSyncExternalStore ( subscribe , getSnapshot , ( ) => {
184
+ initialState = true ;
185
+
186
+ // Important! We must return the same value for both server and client to avoid unnecessary render
187
+ return getSnapshot ( ) ;
188
+ } ) ;
189
+ let [ isSSR , setIsSSR ] = useState ( initialState ) ;
190
+
191
+ if ( typeof document !== 'undefined' ) {
192
+ // eslint-disable-next-line react-hooks/rules-of-hooks
193
+ useLayoutEffect ( ( ) => {
194
+ if ( ! isSSR ) {
195
+ return ;
196
+ }
197
+
198
+ React . startTransition ( ( ) => {
199
+ setIsSSR ( false ) ;
200
+ } ) ;
201
+ } , [ isSSR ] ) ;
193
202
}
194
203
195
- // eslint-disable-next-line react-hooks/rules-of-hooks
204
+ return isSSR ;
205
+ }
206
+
207
+ function useLegacyIsSSR ( ) : boolean {
196
208
return useContext ( IsSSRContext ) ;
197
209
}
210
+
211
+ // Use React.useSyncExternalStore and React.startTransition in React >=18 if available, otherwise fall back to our old implementation.
212
+ /**
213
+ * Returns whether the component is currently being server side rendered or
214
+ * hydrated on the client. Can be used to delay browser-specific rendering
215
+ * until after hydration.
216
+ */
217
+ export const useIsSSR : ( ) => boolean = typeof React [ 'startTransition' ] === 'function' ? useModernIsSSR : useLegacyIsSSR ;
0 commit comments