packages/react-reconciler/src/ReactFiberLane.js JAVASCRIPT 1,309 lines View on github.com → Search inside
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */910import type {Fiber, FiberRoot} from './ReactInternalTypes';11import type {Transition} from 'react/src/ReactStartTransition';12import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates';1314// TODO: Ideally these types would be opaque but that doesn't work well with15// our reconciler fork infra, since these leak into non-reconciler packages.1617export type Lanes = number;18export type Lane = number;19export type LaneMap<T> = Array<T>;2021import {22  enableRetryLaneExpiration,23  enableSchedulingProfiler,24  enableTransitionTracing,25  enableUpdaterTracking,26  syncLaneExpirationMs,27  transitionLaneExpirationMs,28  retryLaneExpirationMs,29  disableLegacyMode,30  enableDefaultTransitionIndicator,31  enableGestureTransition,32  enableParallelTransitions,33} from 'shared/ReactFeatureFlags';34import {isDevToolsPresent} from './ReactFiberDevToolsHook';35import {clz32} from './clz32';36import {LegacyRoot} from './ReactRootTags';3738// Lane values below should be kept in sync with getLabelForLane(), used by react-devtools-timeline.39// If those values are changed that package should be rebuilt and redeployed.4041export const TotalLanes = 31;4243export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;44export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;4546export const SyncHydrationLane: Lane = /*               */ 0b0000000000000000000000000000001;47export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000010;48export const SyncLaneIndex: number = 1;4950export const InputContinuousHydrationLane: Lane = /*    */ 0b0000000000000000000000000000100;51export const InputContinuousLane: Lane = /*             */ 0b0000000000000000000000000001000;5253export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000000010000;54export const DefaultLane: Lane = /*                     */ 0b0000000000000000000000000100000;5556export const SyncUpdateLanes: Lane =57  SyncLane | InputContinuousLane | DefaultLane;5859export const GestureLane: Lane = /*                     */ 0b0000000000000000000000001000000;6061const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000000000010000000;62const TransitionLanes: Lanes = /*                       */ 0b0000000001111111111111100000000;63const TransitionLane1: Lane = /*                        */ 0b0000000000000000000000100000000;64const TransitionLane2: Lane = /*                        */ 0b0000000000000000000001000000000;65const TransitionLane3: Lane = /*                        */ 0b0000000000000000000010000000000;66const TransitionLane4: Lane = /*                        */ 0b0000000000000000000100000000000;67const TransitionLane5: Lane = /*                        */ 0b0000000000000000001000000000000;68const TransitionLane6: Lane = /*                        */ 0b0000000000000000010000000000000;69const TransitionLane7: Lane = /*                        */ 0b0000000000000000100000000000000;70const TransitionLane8: Lane = /*                        */ 0b0000000000000001000000000000000;71const TransitionLane9: Lane = /*                        */ 0b0000000000000010000000000000000;72const TransitionLane10: Lane = /*                       */ 0b0000000000000100000000000000000;73const TransitionLane11: Lane = /*                       */ 0b0000000000001000000000000000000;74const TransitionLane12: Lane = /*                       */ 0b0000000000010000000000000000000;75const TransitionLane13: Lane = /*                       */ 0b0000000000100000000000000000000;76const TransitionLane14: Lane = /*                       */ 0b0000000001000000000000000000000;7778export const SomeTransitionLane: Lane = TransitionLane1;7980const TransitionUpdateLanes =81  TransitionLane1 |82  TransitionLane2 |83  TransitionLane3 |84  TransitionLane4 |85  TransitionLane5 |86  TransitionLane6 |87  TransitionLane7 |88  TransitionLane8 |89  TransitionLane9 |90  TransitionLane10;91const TransitionDeferredLanes =92  TransitionLane11 | TransitionLane12 | TransitionLane13 | TransitionLane14;9394const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;95const RetryLane1: Lane = /*                             */ 0b0000000010000000000000000000000;96const RetryLane2: Lane = /*                             */ 0b0000000100000000000000000000000;97const RetryLane3: Lane = /*                             */ 0b0000001000000000000000000000000;98const RetryLane4: Lane = /*                             */ 0b0000010000000000000000000000000;99100export const SomeRetryLane: Lane = RetryLane1;101102export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;103104const NonIdleLanes: Lanes = /*                          */ 0b0000111111111111111111111111111;105106export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;107export const IdleLane: Lane = /*                        */ 0b0010000000000000000000000000000;108109export const OffscreenLane: Lane = /*                   */ 0b0100000000000000000000000000000;110export const DeferredLane: Lane = /*                    */ 0b1000000000000000000000000000000;111112// Any lane that might schedule an update. This is used to detect infinite113// update loops, so it doesn't include hydration lanes or retries.114export const UpdateLanes: Lanes =115  SyncLane | InputContinuousLane | DefaultLane | TransitionUpdateLanes;116117export const HydrationLanes =118  SyncHydrationLane |119  InputContinuousHydrationLane |120  DefaultHydrationLane |121  TransitionHydrationLane |122  SelectiveHydrationLane |123  IdleHydrationLane;124125// This function is used for the experimental timeline (react-devtools-timeline)126// It should be kept in sync with the Lanes values above.127export function getLabelForLane(lane: Lane): string | void {128  if (enableSchedulingProfiler) {129    if (lane & SyncHydrationLane) {130      return 'SyncHydrationLane';131    }132    if (lane & SyncLane) {133      return 'Sync';134    }135    if (lane & InputContinuousHydrationLane) {136      return 'InputContinuousHydration';137    }138    if (lane & InputContinuousLane) {139      return 'InputContinuous';140    }141    if (lane & DefaultHydrationLane) {142      return 'DefaultHydration';143    }144    if (lane & DefaultLane) {145      return 'Default';146    }147    if (lane & TransitionHydrationLane) {148      return 'TransitionHydration';149    }150    if (lane & TransitionLanes) {151      return 'Transition';152    }153    if (lane & RetryLanes) {154      return 'Retry';155    }156    if (lane & SelectiveHydrationLane) {157      return 'SelectiveHydration';158    }159    if (lane & IdleHydrationLane) {160      return 'IdleHydration';161    }162    if (lane & IdleLane) {163      return 'Idle';164    }165    if (lane & OffscreenLane) {166      return 'Offscreen';167    }168    if (lane & DeferredLane) {169      return 'Deferred';170    }171  }172}173174export const NoTimestamp = -1;175176let nextTransitionUpdateLane: Lane = TransitionLane1;177let nextTransitionDeferredLane: Lane = TransitionLane11;178let nextRetryLane: Lane = RetryLane1;179180function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {181  const pendingSyncLanes = lanes & SyncUpdateLanes;182  if (pendingSyncLanes !== 0) {183    return pendingSyncLanes;184  }185  switch (getHighestPriorityLane(lanes)) {186    case SyncHydrationLane:187      return SyncHydrationLane;188    case SyncLane:189      return SyncLane;190    case InputContinuousHydrationLane:191      return InputContinuousHydrationLane;192    case InputContinuousLane:193      return InputContinuousLane;194    case DefaultHydrationLane:195      return DefaultHydrationLane;196    case DefaultLane:197      return DefaultLane;198    case GestureLane:199      return GestureLane;200    case TransitionHydrationLane:201      return TransitionHydrationLane;202    case TransitionLane1:203    case TransitionLane2:204    case TransitionLane3:205    case TransitionLane4:206    case TransitionLane5:207    case TransitionLane6:208    case TransitionLane7:209    case TransitionLane8:210    case TransitionLane9:211    case TransitionLane10:212      if (enableParallelTransitions) {213        return getHighestPriorityLane(lanes);214      }215      return lanes & TransitionUpdateLanes;216    case TransitionLane11:217    case TransitionLane12:218    case TransitionLane13:219    case TransitionLane14:220      return lanes & TransitionDeferredLanes;221    case RetryLane1:222    case RetryLane2:223    case RetryLane3:224    case RetryLane4:225      return lanes & RetryLanes;226    case SelectiveHydrationLane:227      return SelectiveHydrationLane;228    case IdleHydrationLane:229      return IdleHydrationLane;230    case IdleLane:231      return IdleLane;232    case OffscreenLane:233      return OffscreenLane;234    case DeferredLane:235      // This shouldn't be reachable because deferred work is always entangled236      // with something else.237      return NoLanes;238    default:239      if (__DEV__) {240        console.error(241          'Should have found matching lanes. This is a bug in React.',242        );243      }244      // This shouldn't be reachable, but as a fallback, return the entire bitmask.245      return lanes;246  }247}248249export function getNextLanes(250  root: FiberRoot,251  wipLanes: Lanes,252  rootHasPendingCommit: boolean,253): Lanes {254  // Early bailout if there's no pending work left.255  const pendingLanes = root.pendingLanes;256  if (pendingLanes === NoLanes) {257    return NoLanes;258  }259260  let nextLanes: Lanes = NoLanes;261262  const suspendedLanes = root.suspendedLanes;263  const pingedLanes = root.pingedLanes;264  const warmLanes = root.warmLanes;265266  // finishedLanes represents a completed tree that is ready to commit.267  //268  // It's not worth doing discarding the completed tree in favor of performing269  // speculative work. So always check this before deciding to warm up270  // the siblings.271  //272  // Note that this is not set in a "suspend indefinitely" scenario, like when273  // suspending outside of a Suspense boundary, or in the shell during a274  // transition — only in cases where we are very likely to commit the tree in275  // a brief amount of time (i.e. below the "Just Noticeable Difference"276  // threshold).277  //278279  // Do not work on any idle work until all the non-idle work has finished,280  // even if the work is suspended.281  const nonIdlePendingLanes = pendingLanes & NonIdleLanes;282  if (nonIdlePendingLanes !== NoLanes) {283    // First check for fresh updates.284    const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;285    if (nonIdleUnblockedLanes !== NoLanes) {286      nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);287    } else {288      // No fresh updates. Check if suspended work has been pinged.289      const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;290      if (nonIdlePingedLanes !== NoLanes) {291        nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);292      } else {293        // Nothing has been pinged. Check for lanes that need to be prewarmed.294        if (!rootHasPendingCommit) {295          const lanesToPrewarm = nonIdlePendingLanes & ~warmLanes;296          if (lanesToPrewarm !== NoLanes) {297            nextLanes = getHighestPriorityLanes(lanesToPrewarm);298          }299        }300      }301    }302  } else {303    // The only remaining work is Idle.304    // TODO: Idle isn't really used anywhere, and the thinking around305    // speculative rendering has evolved since this was implemented. Consider306    // removing until we've thought about this again.307308    // First check for fresh updates.309    const unblockedLanes = pendingLanes & ~suspendedLanes;310    if (unblockedLanes !== NoLanes) {311      nextLanes = getHighestPriorityLanes(unblockedLanes);312    } else {313      // No fresh updates. Check if suspended work has been pinged.314      if (pingedLanes !== NoLanes) {315        nextLanes = getHighestPriorityLanes(pingedLanes);316      } else {317        // Nothing has been pinged. Check for lanes that need to be prewarmed.318        if (!rootHasPendingCommit) {319          const lanesToPrewarm = pendingLanes & ~warmLanes;320          if (lanesToPrewarm !== NoLanes) {321            nextLanes = getHighestPriorityLanes(lanesToPrewarm);322          }323        }324      }325    }326  }327328  if (nextLanes === NoLanes) {329    // This should only be reachable if we're suspended330    // TODO: Consider warning in this path if a fallback timer is not scheduled.331    return NoLanes;332  }333334  // If we're already in the middle of a render, switching lanes will interrupt335  // it and we'll lose our progress. We should only do this if the new lanes are336  // higher priority.337  if (338    wipLanes !== NoLanes &&339    wipLanes !== nextLanes &&340    // If we already suspended with a delay, then interrupting is fine. Don't341    // bother waiting until the root is complete.342    (wipLanes & suspendedLanes) === NoLanes343  ) {344    const nextLane = getHighestPriorityLane(nextLanes);345    const wipLane = getHighestPriorityLane(wipLanes);346    if (347      // Tests whether the next lane is equal or lower priority than the wip348      // one. This works because the bits decrease in priority as you go left.349      nextLane >= wipLane ||350      // Default priority updates should not interrupt transition updates. The351      // only difference between default updates and transition updates is that352      // default updates do not support refresh transitions.353      (nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)354    ) {355      // Keep working on the existing in-progress tree. Do not interrupt.356      return wipLanes;357    }358  }359360  return nextLanes;361}362363export function getNextLanesToFlushSync(364  root: FiberRoot,365  extraLanesToForceSync: Lane | Lanes,366): Lanes {367  // Similar to getNextLanes, except instead of choosing the next lanes to work368  // on based on their priority, it selects all the lanes that have equal or369  // higher priority than those are given. That way they can be synchronously370  // rendered in a single batch.371  //372  // The main use case is updates scheduled by popstate events, which are373  // flushed synchronously even though they are transitions.374  // Note that we intentionally treat this as a sync flush to include any375  // sync updates in a single pass but also intentionally disables View Transitions376  // inside popstate. Because they can start synchronously before scroll restoration377  // happens.378  const lanesToFlush = SyncUpdateLanes | extraLanesToForceSync;379380  // Early bailout if there's no pending work left.381  const pendingLanes = root.pendingLanes;382  if (pendingLanes === NoLanes) {383    return NoLanes;384  }385386  const suspendedLanes = root.suspendedLanes;387  const pingedLanes = root.pingedLanes;388389  // Remove lanes that are suspended (but not pinged)390  const unblockedLanes = pendingLanes & ~(suspendedLanes & ~pingedLanes);391  const unblockedLanesWithMatchingPriority =392    unblockedLanes & getLanesOfEqualOrHigherPriority(lanesToFlush);393394  // If there are matching hydration lanes, we should do those by themselves.395  // Hydration lanes must never include updates.396  if (unblockedLanesWithMatchingPriority & HydrationLanes) {397    return (398      (unblockedLanesWithMatchingPriority & HydrationLanes) | SyncHydrationLane399    );400  }401402  if (unblockedLanesWithMatchingPriority) {403    // Always include the SyncLane as part of the result, even if there's no404    // pending sync work, to indicate the priority of the entire batch of work405    // is considered Sync.406    return unblockedLanesWithMatchingPriority | SyncLane;407  }408409  return NoLanes;410}411412export function checkIfRootIsPrerendering(413  root: FiberRoot,414  renderLanes: Lanes,415): boolean {416  const pendingLanes = root.pendingLanes;417  const suspendedLanes = root.suspendedLanes;418  const pingedLanes = root.pingedLanes;419  // Remove lanes that are suspended (but not pinged)420  const unblockedLanes = pendingLanes & ~(suspendedLanes & ~pingedLanes);421422  // If there are no unsuspended or pinged lanes, that implies that we're423  // performing a prerender.424  return (unblockedLanes & renderLanes) === 0;425}426427export function getEntangledLanes(root: FiberRoot, renderLanes: Lanes): Lanes {428  let entangledLanes = renderLanes;429430  if ((entangledLanes & InputContinuousLane) !== NoLanes) {431    // When updates are sync by default, we entangle continuous priority updates432    // and default updates, so they render in the same batch. The only reason433    // they use separate lanes is because continuous updates should interrupt434    // transitions, but default updates should not.435    entangledLanes |= entangledLanes & DefaultLane;436  }437438  // Check for entangled lanes and add them to the batch.439  //440  // A lane is said to be entangled with another when it's not allowed to render441  // in a batch that does not also include the other lane. Typically we do this442  // when multiple updates have the same source, and we only want to respond to443  // the most recent event from that source.444  //445  // Note that we apply entanglements *after* checking for partial work above.446  // This means that if a lane is entangled during an interleaved event while447  // it's already rendering, we won't interrupt it. This is intentional, since448  // entanglement is usually "best effort": we'll try our best to render the449  // lanes in the same batch, but it's not worth throwing out partially450  // completed work in order to do it.451  // TODO: Reconsider this. The counter-argument is that the partial work452  // represents an intermediate state, which we don't want to show to the user.453  // And by spending extra time finishing it, we're increasing the amount of454  // time it takes to show the final state, which is what they are actually455  // waiting for.456  //457  // For those exceptions where entanglement is semantically important,458  // we should ensure that there is no partial work at the459  // time we apply the entanglement.460  const allEntangledLanes = root.entangledLanes;461  if (allEntangledLanes !== NoLanes) {462    const entanglements = root.entanglements;463    let lanes = entangledLanes & allEntangledLanes;464    while (lanes > 0) {465      const index = pickArbitraryLaneIndex(lanes);466      const lane = 1 << index;467468      entangledLanes |= entanglements[index];469470      lanes &= ~lane;471    }472  }473474  return entangledLanes;475}476477function computeExpirationTime(lane: Lane, currentTime: number) {478  switch (lane) {479    case SyncHydrationLane:480    case SyncLane:481    case InputContinuousHydrationLane:482    case InputContinuousLane:483    case GestureLane:484      // User interactions should expire slightly more quickly.485      //486      // NOTE: This is set to the corresponding constant as in Scheduler.js.487      // When we made it larger, a product metric in www regressed, suggesting488      // there's a user interaction that's being starved by a series of489      // synchronous updates. If that theory is correct, the proper solution is490      // to fix the starvation. However, this scenario supports the idea that491      // expiration times are an important safeguard when starvation492      // does happen.493      return currentTime + syncLaneExpirationMs;494    case DefaultHydrationLane:495    case DefaultLane:496    case TransitionHydrationLane:497    case TransitionLane1:498    case TransitionLane2:499    case TransitionLane3:500    case TransitionLane4:501    case TransitionLane5:502    case TransitionLane6:503    case TransitionLane7:504    case TransitionLane8:505    case TransitionLane9:506    case TransitionLane10:507    case TransitionLane11:508    case TransitionLane12:509    case TransitionLane13:510    case TransitionLane14:511      return currentTime + transitionLaneExpirationMs;512    case RetryLane1:513    case RetryLane2:514    case RetryLane3:515    case RetryLane4:516      // TODO: Retries should be allowed to expire if they are CPU bound for517      // too long, but when I made this change it caused a spike in browser518      // crashes. There must be some other underlying bug; not super urgent but519      // ideally should figure out why and fix it. Unfortunately we don't have520      // a repro for the crashes, only detected via production metrics.521      return enableRetryLaneExpiration522        ? currentTime + retryLaneExpirationMs523        : NoTimestamp;524    case SelectiveHydrationLane:525    case IdleHydrationLane:526    case IdleLane:527    case OffscreenLane:528    case DeferredLane:529      // Anything idle priority or lower should never expire.530      return NoTimestamp;531    default:532      if (__DEV__) {533        console.error(534          'Should have found matching lanes. This is a bug in React.',535        );536      }537      return NoTimestamp;538  }539}540541export function markStarvedLanesAsExpired(542  root: FiberRoot,543  currentTime: number,544): void {545  // TODO: This gets called every time we yield. We can optimize by storing546  // the earliest expiration time on the root. Then use that to quickly bail out547  // of this function.548549  const pendingLanes = root.pendingLanes;550  const suspendedLanes = root.suspendedLanes;551  const pingedLanes = root.pingedLanes;552  const expirationTimes = root.expirationTimes;553554  // Iterate through the pending lanes and check if we've reached their555  // expiration time. If so, we'll assume the update is being starved and mark556  // it as expired to force it to finish.557  // TODO: We should be able to replace this with upgradePendingLanesToSync558  //559  // We exclude retry lanes because those must always be time sliced, in order560  // to unwrap uncached promises.561  // TODO: Write a test for this562  let lanes = enableRetryLaneExpiration563    ? pendingLanes564    : pendingLanes & ~RetryLanes;565  while (lanes > 0) {566    const index = pickArbitraryLaneIndex(lanes);567    const lane = 1 << index;568569    const expirationTime = expirationTimes[index];570    if (expirationTime === NoTimestamp) {571      // Found a pending lane with no expiration time. If it's not suspended, or572      // if it's pinged, assume it's CPU-bound. Compute a new expiration time573      // using the current time.574      if (575        (lane & suspendedLanes) === NoLanes ||576        (lane & pingedLanes) !== NoLanes577      ) {578        // Assumes timestamps are monotonically increasing.579        expirationTimes[index] = computeExpirationTime(lane, currentTime);580      }581    } else if (expirationTime <= currentTime) {582      // This lane expired583      root.expiredLanes |= lane;584    }585586    lanes &= ~lane;587  }588}589590// This returns the highest priority pending lanes regardless of whether they591// are suspended.592export function getHighestPriorityPendingLanes(root: FiberRoot): Lanes {593  return getHighestPriorityLanes(root.pendingLanes);594}595596export function getLanesToRetrySynchronouslyOnError(597  root: FiberRoot,598  originallyAttemptedLanes: Lanes,599): Lanes {600  if (root.errorRecoveryDisabledLanes & originallyAttemptedLanes) {601    // The error recovery mechanism is disabled until these lanes are cleared.602    return NoLanes;603  }604605  const everythingButOffscreen = root.pendingLanes & ~OffscreenLane;606  if (everythingButOffscreen !== NoLanes) {607    return everythingButOffscreen;608  }609  if (everythingButOffscreen & OffscreenLane) {610    return OffscreenLane;611  }612  return NoLanes;613}614615export function includesSyncLane(lanes: Lanes): boolean {616  return (lanes & (SyncLane | SyncHydrationLane)) !== NoLanes;617}618619export function includesNonIdleWork(lanes: Lanes): boolean {620  return (lanes & NonIdleLanes) !== NoLanes;621}622export function includesOnlyRetries(lanes: Lanes): boolean {623  return (lanes & RetryLanes) === lanes;624}625export function includesOnlyNonUrgentLanes(lanes: Lanes): boolean {626  // TODO: Should hydration lanes be included here? This function is only627  // used in `updateDeferredValueImpl`.628  const UrgentLanes =629    SyncLane | InputContinuousLane | DefaultLane | GestureLane;630  return (lanes & UrgentLanes) === NoLanes;631}632export function includesOnlyTransitions(lanes: Lanes): boolean {633  return (lanes & TransitionLanes) === lanes;634}635636export function includesTransitionLane(lanes: Lanes): boolean {637  return (lanes & TransitionLanes) !== NoLanes;638}639640export function includesRetryLane(lanes: Lanes): boolean {641  return (lanes & RetryLanes) !== NoLanes;642}643644export function includesIdleGroupLanes(lanes: Lanes): boolean {645  return (646    (lanes &647      (SelectiveHydrationLane |648        IdleHydrationLane |649        IdleLane |650        OffscreenLane |651        DeferredLane)) !==652    NoLanes653  );654}655656export function includesOnlyHydrationLanes(lanes: Lanes): boolean {657  return (lanes & HydrationLanes) === lanes;658}659660export function includesOnlyOffscreenLanes(lanes: Lanes): boolean {661  return (lanes & OffscreenLane) === lanes;662}663664export function includesOnlyHydrationOrOffscreenLanes(lanes: Lanes): boolean {665  return (lanes & (HydrationLanes | OffscreenLane)) === lanes;666}667668export function includesOnlyViewTransitionEligibleLanes(lanes: Lanes): boolean {669  return (lanes & (TransitionLanes | RetryLanes | IdleLane)) === lanes;670}671672export function includesOnlySuspenseyCommitEligibleLanes(673  lanes: Lanes,674): boolean {675  return (676    (lanes & (TransitionLanes | RetryLanes | IdleLane | GestureLane)) === lanes677  );678}679680export function includesLoadingIndicatorLanes(lanes: Lanes): boolean {681  return (lanes & (SyncLane | DefaultLane)) !== NoLanes;682}683684export function includesBlockingLane(lanes: Lanes): boolean {685  const SyncDefaultLanes =686    SyncHydrationLane |687    SyncLane |688    InputContinuousHydrationLane |689    InputContinuousLane |690    DefaultHydrationLane |691    DefaultLane |692    GestureLane;693  return (lanes & SyncDefaultLanes) !== NoLanes;694}695696export function includesExpiredLane(root: FiberRoot, lanes: Lanes): boolean {697  // This is a separate check from includesBlockingLane because a lane can698  // expire after a render has already started.699  return (lanes & root.expiredLanes) !== NoLanes;700}701702export function isBlockingLane(lane: Lane): boolean {703  const SyncDefaultLanes =704    SyncHydrationLane |705    SyncLane |706    InputContinuousHydrationLane |707    InputContinuousLane |708    DefaultHydrationLane |709    DefaultLane |710    GestureLane;711  return (lane & SyncDefaultLanes) !== NoLanes;712}713714export function isTransitionLane(lane: Lane): boolean {715  return (lane & TransitionLanes) !== NoLanes;716}717718export function isGestureRender(lanes: Lanes): boolean {719  if (!enableGestureTransition) {720    return false;721  }722  // This should render only the one lane.723  return lanes === GestureLane;724}725726export function claimNextTransitionUpdateLane(): Lane {727  // Cycle through the lanes, assigning each new transition to the next lane.728  // In most cases, this means every transition gets its own lane, until we729  // run out of lanes and cycle back to the beginning.730  const lane = nextTransitionUpdateLane;731  nextTransitionUpdateLane <<= 1;732  if ((nextTransitionUpdateLane & TransitionUpdateLanes) === NoLanes) {733    nextTransitionUpdateLane = TransitionLane1;734  }735  return lane;736}737738export function claimNextTransitionDeferredLane(): Lane {739  const lane = nextTransitionDeferredLane;740  nextTransitionDeferredLane <<= 1;741  if ((nextTransitionDeferredLane & TransitionDeferredLanes) === NoLanes) {742    nextTransitionDeferredLane = TransitionLane11;743  }744  return lane;745}746747export function claimNextRetryLane(): Lane {748  const lane = nextRetryLane;749  nextRetryLane <<= 1;750  if ((nextRetryLane & RetryLanes) === NoLanes) {751    nextRetryLane = RetryLane1;752  }753  return lane;754}755756export function getHighestPriorityLane(lanes: Lanes): Lane {757  return lanes & -lanes;758}759760function getLanesOfEqualOrHigherPriority(lanes: Lane | Lanes): Lanes {761  // Create a mask with all bits to the right or same as the highest bit.762  // So if lanes is 0b100, the result would be 0b111.763  // If lanes is 0b101, the result would be 0b111.764  const lowestPriorityLaneIndex = 31 - clz32(lanes);765  return (1 << (lowestPriorityLaneIndex + 1)) - 1;766}767768export function pickArbitraryLane(lanes: Lanes): Lane {769  // This wrapper function gets inlined. Only exists so to communicate that it770  // doesn't matter which bit is selected; you can pick any bit without771  // affecting the algorithms where its used. Here I'm using772  // getHighestPriorityLane because it requires the fewest operations.773  return getHighestPriorityLane(lanes);774}775776function pickArbitraryLaneIndex(lanes: Lanes) {777  return 31 - clz32(lanes);778}779780function laneToIndex(lane: Lane) {781  return pickArbitraryLaneIndex(lane);782}783784export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane): boolean {785  return (a & b) !== NoLanes;786}787788export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane): boolean {789  return (set & subset) === subset;790}791792export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {793  return a | b;794}795796export function removeLanes(set: Lanes, subset: Lanes | Lane): Lanes {797  return set & ~subset;798}799800export function intersectLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {801  return a & b;802}803804// Seems redundant, but it changes the type from a single lane (used for805// updates) to a group of lanes (used for flushing work).806export function laneToLanes(lane: Lane): Lanes {807  return lane;808}809810export function higherPriorityLane(a: Lane, b: Lane): Lane {811  // This works because the bit ranges decrease in priority as you go left.812  return a !== NoLane && a < b ? a : b;813}814815export function createLaneMap<T>(initial: T): LaneMap<T> {816  // Intentionally pushing one by one.817  // https://v8.dev/blog/elements-kinds#avoid-creating-holes818  const laneMap = [];819  for (let i = 0; i < TotalLanes; i++) {820    laneMap.push(initial);821  }822  return laneMap;823}824825export function markRootUpdated(root: FiberRoot, updateLane: Lane) {826  root.pendingLanes |= updateLane;827  if (enableDefaultTransitionIndicator) {828    // Mark that this lane might need a loading indicator to be shown.829    root.indicatorLanes |= updateLane & TransitionLanes;830  }831832  // If there are any suspended transitions, it's possible this new update833  // could unblock them. Clear the suspended lanes so that we can try rendering834  // them again.835  //836  // TODO: We really only need to unsuspend only lanes that are in the837  // `subtreeLanes` of the updated fiber, or the update lanes of the return838  // path. This would exclude suspended updates in an unrelated sibling tree,839  // since there's no way for this update to unblock it.840  //841  // We don't do this if the incoming update is idle, because we never process842  // idle updates until after all the regular updates have finished; there's no843  // way it could unblock a transition.844  if (updateLane !== IdleLane) {845    root.suspendedLanes = NoLanes;846    root.pingedLanes = NoLanes;847    root.warmLanes = NoLanes;848  }849}850851export function markRootSuspended(852  root: FiberRoot,853  suspendedLanes: Lanes,854  spawnedLane: Lane,855  didAttemptEntireTree: boolean,856) {857  // TODO: Split this into separate functions for marking the root at the end of858  // a render attempt versus suspending while the root is still in progress.859  root.suspendedLanes |= suspendedLanes;860  root.pingedLanes &= ~suspendedLanes;861862  if (didAttemptEntireTree) {863    // Mark these lanes as warm so we know there's nothing else to work on.864    root.warmLanes |= suspendedLanes;865  } else {866    // Render unwound without attempting all the siblings. Do no mark the lanes867    // as warm. This will cause a prewarm render to be scheduled.868  }869870  // The suspended lanes are no longer CPU-bound. Clear their expiration times.871  const expirationTimes = root.expirationTimes;872  let lanes = suspendedLanes;873  while (lanes > 0) {874    const index = pickArbitraryLaneIndex(lanes);875    const lane = 1 << index;876877    expirationTimes[index] = NoTimestamp;878879    lanes &= ~lane;880  }881882  if (spawnedLane !== NoLane) {883    markSpawnedDeferredLane(root, spawnedLane, suspendedLanes);884  }885}886887export function markRootPinged(root: FiberRoot, pingedLanes: Lanes) {888  root.pingedLanes |= root.suspendedLanes & pingedLanes;889  // The data that just resolved could have unblocked additional children, which890  // will also need to be prewarmed if something suspends again.891  root.warmLanes &= ~pingedLanes;892}893894export function markRootFinished(895  root: FiberRoot,896  finishedLanes: Lanes,897  remainingLanes: Lanes,898  spawnedLane: Lane,899  updatedLanes: Lanes,900  suspendedRetryLanes: Lanes,901) {902  const previouslyPendingLanes = root.pendingLanes;903  const noLongerPendingLanes = previouslyPendingLanes & ~remainingLanes;904905  root.pendingLanes = remainingLanes;906907  // Let's try everything again908  root.suspendedLanes = NoLanes;909  root.pingedLanes = NoLanes;910  root.warmLanes = NoLanes;911912  if (enableDefaultTransitionIndicator) {913    root.indicatorLanes &= remainingLanes;914  }915916  root.expiredLanes &= remainingLanes;917918  root.entangledLanes &= remainingLanes;919920  root.errorRecoveryDisabledLanes &= remainingLanes;921  root.shellSuspendCounter = 0;922923  const entanglements = root.entanglements;924  const expirationTimes = root.expirationTimes;925  const hiddenUpdates = root.hiddenUpdates;926927  // Clear the lanes that no longer have pending work928  let lanes = noLongerPendingLanes;929  while (lanes > 0) {930    const index = pickArbitraryLaneIndex(lanes);931    const lane = 1 << index;932933    entanglements[index] = NoLanes;934    expirationTimes[index] = NoTimestamp;935936    const hiddenUpdatesForLane = hiddenUpdates[index];937    if (hiddenUpdatesForLane !== null) {938      hiddenUpdates[index] = null;939      // "Hidden" updates are updates that were made to a hidden component. They940      // have special logic associated with them because they may be entangled941      // with updates that occur outside that tree. But once the outer tree942      // commits, they behave like regular updates.943      for (let i = 0; i < hiddenUpdatesForLane.length; i++) {944        const update = hiddenUpdatesForLane[i];945        // $FlowFixMe[invalid-compare]946        if (update !== null) {947          update.lane &= ~OffscreenLane;948        }949      }950    }951952    lanes &= ~lane;953  }954955  if (spawnedLane !== NoLane) {956    markSpawnedDeferredLane(957      root,958      spawnedLane,959      // This render finished successfully without suspending, so we don't need960      // to entangle the spawned task with the parent task.961      NoLanes,962    );963  }964965  // suspendedRetryLanes represents the retry lanes spawned by new Suspense966  // boundaries during this render that were not later pinged.967  //968  // These lanes were marked as pending on their associated Suspense boundary969  // fiber during the render phase so that we could start rendering them970  // before new data streams in. As soon as the fallback commits, we can try971  // to render them again.972  //973  // But since we know they're still suspended, we can skip straight to the974  // "prerender" mode (i.e. don't skip over siblings after something975  // suspended) instead of the regular mode (i.e. unwind and skip the siblings976  // as soon as something suspends to unblock the rest of the update).977  if (978    suspendedRetryLanes !== NoLanes &&979    // Note that we only do this if there were no updates since we started980    // rendering. This mirrors the logic in markRootUpdated — whenever we981    // receive an update, we reset all the suspended and pinged lanes.982    updatedLanes === NoLanes &&983    !(disableLegacyMode && root.tag === LegacyRoot)984  ) {985    // We also need to avoid marking a retry lane as suspended if it was already986    // pending before this render. We can't say these are now suspended if they987    // weren't included in our attempt.988    const freshlySpawnedRetryLanes =989      suspendedRetryLanes &990      // Remove any retry lane that was already pending before our just-finished991      // attempt, and also wasn't included in that attempt.992      ~(previouslyPendingLanes & ~finishedLanes);993    root.suspendedLanes |= freshlySpawnedRetryLanes;994  }995}996997function markSpawnedDeferredLane(998  root: FiberRoot,999  spawnedLane: Lane,1000  entangledLanes: Lanes,1001) {1002  // This render spawned a deferred task. Mark it as pending.1003  root.pendingLanes |= spawnedLane;1004  root.suspendedLanes &= ~spawnedLane;10051006  // Entangle the spawned lane with the DeferredLane bit so that we know it1007  // was the result of another render. This lets us avoid a useDeferredValue1008  // waterfall — only the first level will defer.1009  // TODO: Now that there is a reserved set of transition lanes that are used1010  // exclusively for deferred work, we should get rid of this special1011  // DeferredLane bit; the same information can be inferred by checking whether1012  // the lane is one of the TransitionDeferredLanes. The only reason this still1013  // exists is because we need to also do the same for OffscreenLane. That1014  // requires additional changes because there are more places around the1015  // codebase that treat OffscreenLane as a magic value; would need to check1016  // for a new OffscreenDeferredLane, too. Will leave this for a follow-up.1017  const spawnedLaneIndex = laneToIndex(spawnedLane);1018  root.entangledLanes |= spawnedLane;1019  root.entanglements[spawnedLaneIndex] |=1020    DeferredLane |1021    // If the parent render task suspended, we must also entangle those lanes1022    // with the spawned task, so that the deferred task includes all the same1023    // updates that the parent task did. We can exclude any lane that is not1024    // used for updates (e.g. Offscreen).1025    (entangledLanes & UpdateLanes);1026}10271028export function markRootEntangled(root: FiberRoot, entangledLanes: Lanes) {1029  // In addition to entangling each of the given lanes with each other, we also1030  // have to consider _transitive_ entanglements. For each lane that is already1031  // entangled with *any* of the given lanes, that lane is now transitively1032  // entangled with *all* the given lanes.1033  //1034  // Translated: If C is entangled with A, then entangling A with B also1035  // entangles C with B.1036  //1037  // If this is hard to grasp, it might help to intentionally break this1038  // function and look at the tests that fail in ReactTransition-test.js. Try1039  // commenting out one of the conditions below.10401041  const rootEntangledLanes = (root.entangledLanes |= entangledLanes);1042  const entanglements = root.entanglements;1043  let lanes = rootEntangledLanes;1044  while (lanes) {1045    const index = pickArbitraryLaneIndex(lanes);1046    const lane = 1 << index;1047    if (1048      // Is this one of the newly entangled lanes?1049      (lane & entangledLanes) |1050      // Is this lane transitively entangled with the newly entangled lanes?1051      (entanglements[index] & entangledLanes)1052    ) {1053      entanglements[index] |= entangledLanes;1054    }1055    lanes &= ~lane;1056  }1057}10581059export function upgradePendingLanesToSync(1060  root: FiberRoot,1061  lanesToUpgrade: Lanes,1062) {1063  // Same as upgradePendingLaneToSync but accepts multiple lanes, so it's a1064  // bit slower.1065  root.pendingLanes |= SyncLane;1066  root.entangledLanes |= SyncLane;1067  let lanes = lanesToUpgrade;1068  while (lanes) {1069    const index = pickArbitraryLaneIndex(lanes);1070    const lane = 1 << index;1071    root.entanglements[SyncLaneIndex] |= lane;1072    lanes &= ~lane;1073  }1074}10751076export function markHiddenUpdate(1077  root: FiberRoot,1078  update: ConcurrentUpdate,1079  lane: Lane,1080) {1081  const index = laneToIndex(lane);1082  const hiddenUpdates = root.hiddenUpdates;1083  const hiddenUpdatesForLane = hiddenUpdates[index];1084  if (hiddenUpdatesForLane === null) {1085    hiddenUpdates[index] = [update];1086  } else {1087    hiddenUpdatesForLane.push(update);1088  }1089  update.lane = lane | OffscreenLane;1090}10911092export function getBumpedLaneForHydration(1093  root: FiberRoot,1094  renderLanes: Lanes,1095): Lane {1096  const renderLane = getHighestPriorityLane(renderLanes);1097  const bumpedLane =1098    (renderLane & SyncUpdateLanes) !== NoLane1099      ? // Unify sync lanes. We don't do this inside getBumpedLaneForHydrationByLane1100        // because that causes things to flush synchronously when they shouldn't.1101        // TODO: This is not coherent but that's beacuse the unification is not coherent.1102        // We need to get merge these into an actual single lane.1103        SyncHydrationLane1104      : getBumpedLaneForHydrationByLane(renderLane);1105  // Check if the lane we chose is suspended. If so, that indicates that we1106  // already attempted and failed to hydrate at that level. Also check if we're1107  // already rendering that lane, which is rare but could happen.1108  // TODO: This should move into the caller to decide whether giving up is valid.1109  if ((bumpedLane & (root.suspendedLanes | renderLanes)) !== NoLane) {1110    // Give up trying to hydrate and fall back to client render.1111    return NoLane;1112  }1113  return bumpedLane;1114}11151116export function getBumpedLaneForHydrationByLane(lane: Lane): Lane {1117  switch (lane) {1118    case SyncLane:1119      lane = SyncHydrationLane;1120      break;1121    case InputContinuousLane:1122      lane = InputContinuousHydrationLane;1123      break;1124    case DefaultLane:1125      lane = DefaultHydrationLane;1126      break;1127    case TransitionLane1:1128    case TransitionLane2:1129    case TransitionLane3:1130    case TransitionLane4:1131    case TransitionLane5:1132    case TransitionLane6:1133    case TransitionLane7:1134    case TransitionLane8:1135    case TransitionLane9:1136    case TransitionLane10:1137    case TransitionLane11:1138    case TransitionLane12:1139    case TransitionLane13:1140    case TransitionLane14:1141    case RetryLane1:1142    case RetryLane2:1143    case RetryLane3:1144    case RetryLane4:1145      lane = TransitionHydrationLane;1146      break;1147    case IdleLane:1148      lane = IdleHydrationLane;1149      break;1150    default:1151      // Everything else is already either a hydration lane, or shouldn't1152      // be retried at a hydration lane.1153      lane = NoLane;1154      break;1155  }1156  return lane;1157}11581159export function addFiberToLanesMap(1160  root: FiberRoot,1161  fiber: Fiber,1162  lanes: Lanes | Lane,1163) {1164  if (!enableUpdaterTracking) {1165    return;1166  }1167  if (!isDevToolsPresent) {1168    return;1169  }1170  const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;1171  while (lanes > 0) {1172    const index = laneToIndex(lanes);1173    const lane = 1 << index;11741175    const updaters = pendingUpdatersLaneMap[index];1176    updaters.add(fiber);11771178    lanes &= ~lane;1179  }1180}11811182export function movePendingFibersToMemoized(root: FiberRoot, lanes: Lanes) {1183  if (!enableUpdaterTracking) {1184    return;1185  }1186  if (!isDevToolsPresent) {1187    return;1188  }1189  const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;1190  const memoizedUpdaters = root.memoizedUpdaters;1191  while (lanes > 0) {1192    const index = laneToIndex(lanes);1193    const lane = 1 << index;11941195    const updaters = pendingUpdatersLaneMap[index];1196    if (updaters.size > 0) {1197      updaters.forEach(fiber => {1198        const alternate = fiber.alternate;1199        if (alternate === null || !memoizedUpdaters.has(alternate)) {1200          memoizedUpdaters.add(fiber);1201        }1202      });1203      updaters.clear();1204    }12051206    lanes &= ~lane;1207  }1208}12091210export function addTransitionToLanesMap(1211  root: FiberRoot,1212  transition: Transition,1213  lane: Lane,1214) {1215  if (enableTransitionTracing) {1216    const transitionLanesMap = root.transitionLanes;1217    const index = laneToIndex(lane);1218    let transitions = transitionLanesMap[index];1219    if (transitions === null) {1220      transitions = new Set();1221    }1222    transitions.add(transition);12231224    transitionLanesMap[index] = transitions;1225  }1226}12271228export function getTransitionsForLanes(1229  root: FiberRoot,1230  lanes: Lane | Lanes,1231): Array<Transition> | null {1232  if (!enableTransitionTracing) {1233    return null;1234  }12351236  const transitionsForLanes = [];1237  while (lanes > 0) {1238    const index = laneToIndex(lanes);1239    const lane = 1 << index;1240    const transitions = root.transitionLanes[index];1241    if (transitions !== null) {1242      transitions.forEach(transition => {1243        transitionsForLanes.push(transition);1244      });1245    }12461247    lanes &= ~lane;1248  }12491250  if (transitionsForLanes.length === 0) {1251    return null;1252  }12531254  return transitionsForLanes;1255}12561257export function clearTransitionsForLanes(root: FiberRoot, lanes: Lane | Lanes) {1258  if (!enableTransitionTracing) {1259    return;1260  }12611262  while (lanes > 0) {1263    const index = laneToIndex(lanes);1264    const lane = 1 << index;12651266    const transitions = root.transitionLanes[index];1267    if (transitions !== null) {1268      root.transitionLanes[index] = null;1269    }12701271    lanes &= ~lane;1272  }1273}12741275// Used to name the Performance Track1276export function getGroupNameOfHighestPriorityLane(lanes: Lanes): string {1277  if (1278    lanes &1279    (SyncHydrationLane |1280      SyncLane |1281      InputContinuousHydrationLane |1282      InputContinuousLane |1283      DefaultHydrationLane |1284      DefaultLane)1285  ) {1286    return 'Blocking';1287  }1288  if (lanes & GestureLane) {1289    return 'Gesture';1290  }1291  if (lanes & (TransitionHydrationLane | TransitionLanes)) {1292    return 'Transition';1293  }1294  if (lanes & RetryLanes) {1295    return 'Suspense';1296  }1297  if (1298    lanes &1299    (SelectiveHydrationLane |1300      IdleHydrationLane |1301      IdleLane |1302      OffscreenLane |1303      DeferredLane)1304  ) {1305    return 'Idle';1306  }1307  return 'Other';1308}

Code quality findings 65

Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (pendingSyncLanes !== 0) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (getHighestPriorityLane(lanes)) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (pendingLanes === NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nonIdlePendingLanes !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nonIdleUnblockedLanes !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nonIdlePingedLanes !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (lanesToPrewarm !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (unblockedLanes !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (pingedLanes !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (lanesToPrewarm !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (nextLanes === NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
wipLanes !== NoLanes &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
wipLanes !== nextLanes &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(wipLanes & suspendedLanes) === NoLanes
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (pendingLanes === NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (unblockedLanes & renderLanes) === 0;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((entangledLanes & InputContinuousLane) !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (allEntangledLanes !== NoLanes) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (lane) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (expirationTime === NoTimestamp) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(lane & suspendedLanes) === NoLanes ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(lane & pingedLanes) !== NoLanes
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (everythingButOffscreen !== NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & (SyncLane | SyncHydrationLane)) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & NonIdleLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & RetryLanes) === lanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & UrgentLanes) === NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & TransitionLanes) === lanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & TransitionLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & RetryLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & HydrationLanes) === lanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & OffscreenLane) === lanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & (HydrationLanes | OffscreenLane)) === lanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & (TransitionLanes | RetryLanes | IdleLane)) === lanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(lanes & (TransitionLanes | RetryLanes | IdleLane | GestureLane)) === lanes
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & (SyncLane | DefaultLane)) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & SyncDefaultLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lanes & root.expiredLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lane & SyncDefaultLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (lane & TransitionLanes) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return lanes === GestureLane;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((nextTransitionUpdateLane & TransitionUpdateLanes) === NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((nextTransitionDeferredLane & TransitionDeferredLanes) === NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((nextRetryLane & RetryLanes) === NoLanes) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (a & b) !== NoLanes;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return (set & subset) === subset;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return a !== NoLane && a < b ? a : b;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (updateLane !== IdleLane) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (spawnedLane !== NoLane) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (hiddenUpdatesForLane !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (update !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (spawnedLane !== NoLane) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
suspendedRetryLanes !== NoLanes &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
updatedLanes === NoLanes &&
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
!(disableLegacyMode && root.tag === LegacyRoot)
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (hiddenUpdatesForLane === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
(renderLane & SyncUpdateLanes) !== NoLane
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if ((bumpedLane & (root.suspendedLanes | renderLanes)) !== NoLane) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (lane) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (alternate === null || !memoizedUpdaters.has(alternate)) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (transitions === null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (transitions !== null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (transitionsForLanes.length === 0) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (transitions !== null) {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.