Repository: facebook/react
File: packages/react-devtools-shared/src/backend/fiber/renderer.js (JavaScript)

Code before:
      fiberInstance.flags &= ~FORCE_ERROR;
      forceErrorCount--;
      if (forceErrorCount === 0 && setErrorHandler != null) {
        setErrorHandler(shouldErrorFiberAlwaysNull);
      }
    }
    if (fiberInstance.flags & FORCE_SUSPENSE_FALLBACK) {
      fiberInstance.flags &= ~FORCE_SUSPENSE_FALLBACK;
      forceFallbackCount--;
      if (forceFallbackCount === 0 && setSuspenseHandler != null) {
        setSuspenseHandler(shouldSuspendFiberAlwaysFalse);
      }
    }

    if (fiberToFiberInstanceMap.get(fiber) === fiberInstance) {
      fiberToFiberInstanceMap.delete(fiber);
    }
    const {alternate} = fiber;
    if (alternate !== null) {
      if (fiberToFiberInstanceMap.get(alternate) === fiberInstance) {
        fiberToFiberInstanceMap.delete(alternate);
      }
    }

    // TODO: This is not enough if this Fiber was filtered since we don't end up
    // untracking it. We could use a WeakMap but that doesn't work for Paper tags.
    if (fiber.tag === HostHoistable) {
      releaseHostResource(fiberInstance, fiber.memoizedState);
    } else if (
      fiber.tag === HostComponent ||
      fiber.tag === HostText ||
      fiber.tag === HostSingleton
    ) {
      releaseHostInstance(fiberInstance, fiber.stateNode);
    }
  }

  function getChangeDescription(
    prevFiber: Fiber | null,
    nextFiber: Fiber,
  ): ChangeDescription | null {
    switch (getElementTypeForFiber(nextFiber)) {
      case ElementTypeClass:
      case ElementTypeFunction:
      case ElementTypeMemo:
      case ElementTypeForwardRef:
        if (prevFiber === null) {
          return {
            context: null,
            didHooksChange: false,
            isFirstMount: true,

Review comment (suggestion):
This is not really new. I discovered this was already an issue with leaking HostHoistables but it's really a blocker for landing this PR since it leaks DOM nodes if they're filtered which is way more common.

I can address this with a separate PR though which I think will need to start tracking some filtered Fiber Instances in some cases.

Code after:
    const fiberInstance = getFiberInstanceUnsafe(fiber);
    if (fiberInstance !== null) {
      return fiberInstance;
    }
    throw Error(
      `Could not find ID for Fiber "${getDisplayNameForFiber(fiber) || ''}"`,
    );
  }

  function getFiberIDThrows(fiber: Fiber): number {
    const fiberInstance = getFiberInstanceUnsafe(fiber);
    if (fiberInstance !== null) {
      return fiberInstance.id;
    }
    throw Error(
      `Could not find ID for Fiber "${getDisplayNameForFiber(fiber) || ''}"`,
    );
  }

  // Returns a FiberInstance if one has already been generated for the Fiber or null if one has not been generated.
  // Use this method while e.g. logging to avoid over-retaining Fibers.
  function getFiberInstanceUnsafe(fiber: Fiber): FiberInstance | null {
    const fiberInstance = fiberToFiberInstanceMap.get(fiber);
    if (fiberInstance !== undefined) {
      return fiberInstance;
    } else {
      const {alternate} = fiber;
      if (alternate !== null) {
        const alternateInstance = fiberToFiberInstanceMap.get(alternate);
        if (alternateInstance !== undefined) {
          return alternateInstance;
        }
      }
    }
    return null;
  }

  function getFiberIDUnsafe(fiber: Fiber): number | null {
    const fiberInstance = getFiberInstanceUnsafe(fiber);
    return fiberInstance === null ? null : fiberInstance.id;
  }

  // Removes a Fiber (and its alternate) from the Maps used to track their id.
  // This method should always be called when a Fiber is unmounting.
  function untrackFiber(nearestInstance: DevToolsInstance, fiber: Fiber) {
    if (__DEBUG__) {
      debug('untrackFiber()', fiber, null);
    }
    // TODO: Consider using a WeakMap instead. The only thing where that doesn't work
    // is React Native Paper which tracks tags but that support is eventually going away
    // and can use the old findFiberByHostInstance strategy.