@@ -163,6 +163,21 @@ function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(
163163
164164 useEffect ( ( ) => ( ) => onyxSnapshotCache . deregisterConsumer ( key , cacheKey ) , [ key , cacheKey ] ) ;
165165
166+ // Precompute whether this key is a skippable collection member so that getSnapshot()
167+ // can use a cheap boolean check instead of calling splitCollectionMemberKey on every render.
168+ const isSkippableKey = useMemo ( ( ) => {
169+ const skippableIDs = OnyxUtils . getSkippableCollectionMemberIDs ( ) ;
170+ if ( ! skippableIDs . size ) {
171+ return false ;
172+ }
173+ try {
174+ const [ , memberId ] = OnyxUtils . splitCollectionMemberKey ( key ) ;
175+ return skippableIDs . has ( memberId ) ;
176+ } catch {
177+ return false ;
178+ }
179+ } , [ key ] ) ;
180+
166181 useEffect ( ( ) => {
167182 // These conditions will ensure we can only handle dynamic collection member keys from the same collection.
168183 if ( options ?. allowDynamicKey || previousKey === key ) {
@@ -231,23 +246,16 @@ function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(
231246 } , [ key , options ?. canEvict ] ) ;
232247
233248 const getSnapshot = useCallback ( ( ) => {
234- // Fast path: if subscribing to a skippable collection member id, return undefined as loaded immediately
235- if ( isFirstConnectionRef . current ) {
236- try {
237- const [ , memberId ] = OnyxUtils . splitCollectionMemberKey ( key ) ;
238- if ( OnyxUtils . getSkippableCollectionMemberIDs ( ) . has ( memberId ) ) {
239- // Finalize initial state as loaded undefined and stop further first-connection flows
240- if ( resultRef . current [ 1 ] . status !== 'loaded' || resultRef . current [ 0 ] !== undefined ) {
241- resultRef . current = [ undefined , { status : 'loaded' } ] ;
242- onyxSnapshotCache . setCachedResult < UseOnyxResult < TReturnValue > > ( key , cacheKey , resultRef . current ) ;
243- }
244- isFirstConnectionRef . current = false ;
245- shouldGetCachedValueRef . current = false ;
246- return resultRef . current ;
247- }
248- } catch ( e ) {
249- // Not a collection member, continue as usual
249+ // Fast path: if subscribing to a skippable collection member id, return undefined as loaded immediately.
250+ // The `isSkippableKey` flag is precomputed so we avoid calling splitCollectionMemberKey here.
251+ if ( isFirstConnectionRef . current && isSkippableKey ) {
252+ if ( resultRef . current [ 1 ] . status !== 'loaded' || resultRef . current [ 0 ] !== undefined ) {
253+ resultRef . current = [ undefined , { status : 'loaded' } ] ;
254+ onyxSnapshotCache . setCachedResult < UseOnyxResult < TReturnValue > > ( key , cacheKey , resultRef . current ) ;
250255 }
256+ isFirstConnectionRef . current = false ;
257+ shouldGetCachedValueRef . current = false ;
258+ return resultRef . current ;
251259 }
252260 // Check if we have any cache for this Onyx key
253261 // Don't use cache for first connection with initWithStoredValues: false
@@ -349,7 +357,7 @@ function useOnyx<TKey extends OnyxKey, TReturnValue = OnyxValue<TKey>>(
349357 }
350358
351359 return resultRef . current ;
352- } , [ options ?. initWithStoredValues , options ?. allowStaleData , options ?. canBeMissing , key , memoizedSelector , cacheKey , previousKey ] ) ;
360+ } , [ options ?. initWithStoredValues , options ?. allowStaleData , options ?. canBeMissing , key , memoizedSelector , cacheKey , previousKey , isSkippableKey ] ) ;
353361
354362 const subscribe = useCallback (
355363 ( onStoreChange : ( ) => void ) => {
0 commit comments