Skip to content

Commit

Permalink
improve component tree
Browse files Browse the repository at this point in the history
  • Loading branch information
pivanov committed Feb 2, 2025
1 parent 39537da commit 60287ff
Show file tree
Hide file tree
Showing 15 changed files with 630 additions and 873 deletions.
1 change: 1 addition & 0 deletions packages/scan/src/core/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ export const createInstrumentation = (
),
);
}

const { selfTime } = getTimings(fiber);

const fps = getFPS();
Expand Down
4 changes: 1 addition & 3 deletions packages/scan/src/new-outlines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,7 @@ export const initReactScanInstrumentation = () => {
}

if (Store.inspectState.value.kind === 'focused') {
if (isCompositeFiber(fiber)) {
inspectorUpdateSignal.value = Date.now();
}
inspectorUpdateSignal.value = Date.now();
}

ReactScanInternals.options.value.onRender?.(fiber, renders);
Expand Down
2 changes: 2 additions & 0 deletions packages/scan/src/web/components/inspector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export const Inspector = constant(() => {

const { parentCompositeFiber } = getCompositeFiberFromElement(
state.focusedDomElement,
state.fiber
);


Expand All @@ -135,6 +136,7 @@ export const Inspector = constant(() => {

const { parentCompositeFiber } = getCompositeFiberFromElement(
inspectState.focusedDomElement,
inspectState.fiber
);

if (!parentCompositeFiber) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { signalIsSettingsOpen } from '~web/state';
import { cn, throttle } from '~web/utils/helpers';
import { lerp } from '~web/utils/lerp';
import { getFiberPath } from '~web/utils/pin';
import { timelineState } from '../states';

type DrawKind = 'locked' | 'inspecting';
Expand Down
121 changes: 77 additions & 44 deletions packages/scan/src/web/components/inspector/states.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,39 @@ export interface TimelineUpdate {
}

export interface TimelineState {
updates: TimelineUpdate[];
currentIndex: number;
playbackSpeed: 1 | 2 | 4;
updates: Array<TimelineUpdate>;
currentFiber: Fiber | null;
totalUpdates: number;
isVisible: boolean;
windowOffset: number;
currentIndex: number;
isViewingHistory: boolean;
latestFiber: Fiber | null;
isVisible: boolean;
playbackSpeed: 1 | 2 | 4;
}

export const TIMELINE_MAX_UPDATES = 1000;

const timelineStateDefault: TimelineState = {
export const timelineStateDefault: TimelineState = {
updates: [],
currentIndex: 0,
playbackSpeed: 1,
currentFiber: null,
totalUpdates: 0,
isVisible: false,
windowOffset: 0,
currentIndex: 0,
isViewingHistory: false,
latestFiber: null,
isVisible: false,
playbackSpeed: 1,
};

export const timelineState = signal<TimelineState>(timelineStateDefault);

export const inspectorUpdateSignal = signal<number>(0);

// Add batching storage
let pendingUpdates: Array<{ update: TimelineUpdate; fiber: Fiber | null }> = [];
let batchTimeout: ReturnType<typeof setTimeout> | null = null;

export const timelineActions = {
showTimeline: () => {
timelineState.value = {
Expand Down Expand Up @@ -82,49 +88,76 @@ export const timelineActions = {
},

addUpdate: (update: TimelineUpdate, latestFiber: Fiber | null) => {
const { updates, totalUpdates, currentIndex, isViewingHistory } =
timelineState.value;

const newUpdates = [...updates];
const newTotalUpdates = totalUpdates + 1;

if (newUpdates.length >= TIMELINE_MAX_UPDATES) {
newUpdates.shift();
}

newUpdates.push(update);

const newWindowOffset = Math.max(0, newTotalUpdates - TIMELINE_MAX_UPDATES);
// Collect the update
pendingUpdates.push({ update, fiber: latestFiber });

if (!batchTimeout) {
// If no batch is scheduled, schedule one
batchTimeout = setTimeout(() => {
// Process all collected updates
const batchedUpdates = pendingUpdates;
pendingUpdates = [];
batchTimeout = null;

// Apply all updates in the batch
const { updates, totalUpdates, currentIndex, isViewingHistory } =
timelineState.value;
const newUpdates = [...updates];
let newTotalUpdates = totalUpdates;

// Process each update in the batch
for (const { update } of batchedUpdates) {
if (newUpdates.length >= TIMELINE_MAX_UPDATES) {
newUpdates.shift();
}
newUpdates.push(update);
newTotalUpdates++;
}

let newCurrentIndex: number;
if (isViewingHistory) {
if (currentIndex === totalUpdates - 1) {
newCurrentIndex = newUpdates.length - 1;
} else if (currentIndex === 0) {
newCurrentIndex = 0;
} else {
if (newWindowOffset === 0) {
newCurrentIndex = currentIndex;
const newWindowOffset = Math.max(
0,
newTotalUpdates - TIMELINE_MAX_UPDATES,
);

let newCurrentIndex: number;
if (isViewingHistory) {
if (currentIndex === totalUpdates - 1) {
newCurrentIndex = newUpdates.length - 1;
} else if (currentIndex === 0) {
newCurrentIndex = 0;
} else {
if (newWindowOffset === 0) {
newCurrentIndex = currentIndex;
} else {
newCurrentIndex = currentIndex - 1;
}
}
} else {
newCurrentIndex = currentIndex - 1;
newCurrentIndex = newUpdates.length - 1;
}
}
} else {
newCurrentIndex = newUpdates.length - 1;
}

timelineState.value = {
...timelineState.value,
latestFiber,
updates: newUpdates,
totalUpdates: newTotalUpdates,
windowOffset: newWindowOffset,
currentIndex: newCurrentIndex,
isViewingHistory,
};
// Get the latest fiber from the last update in batch
const lastUpdate = batchedUpdates[batchedUpdates.length - 1];

timelineState.value = {
...timelineState.value,
latestFiber: lastUpdate.fiber,
updates: newUpdates,
totalUpdates: newTotalUpdates,
windowOffset: newWindowOffset,
currentIndex: newCurrentIndex,
isViewingHistory,
};
}, 100);
}
},

reset: () => {
if (batchTimeout) {
clearTimeout(batchTimeout);
batchTimeout = null;
}
pendingUpdates = [];
timelineState.value = timelineStateDefault;
},
};
Expand Down
59 changes: 30 additions & 29 deletions packages/scan/src/web/components/inspector/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,22 @@ export const getNearestFiberFromElement = (
}
};

export const getParentCompositeFiber = (fiber: Fiber) => {
let curr: Fiber | null = fiber;
let prevHost = null;
export const getParentCompositeFiber = (
fiber: Fiber,
): readonly [Fiber, Fiber | null] | null => {
let current: Fiber | null = fiber;
let prevHost: Fiber | null = null;

while (curr) {
if (isCompositeFiber(curr)) {
return [curr, prevHost] as const;
}
if (isHostFiber(curr)) {
prevHost = curr;
}
curr = curr.return;
while (current) {
if (isCompositeFiber(current)) return [current, prevHost] as const;
if (isHostFiber(current) && !prevHost) prevHost = current;
current = current.return;
}

return null;
};


const isFiberInTree = (fiber: Fiber, root: Fiber): boolean => {
{
// const root= fiberRootCache.get(fiber) || (fiber.alternate && fiberRootCache.get(fiber.alternate) )
Expand Down Expand Up @@ -221,28 +222,28 @@ export const getCompositeComponentFromElement = (element: Element) => {
};
};

export const getCompositeFiberFromElement = (element: Element) => {
const associatedFiber = getNearestFiberFromElement(element);
export const getCompositeFiberFromElement = (
element: Element,
knownFiber?: Fiber,
) => {
if (!element.isConnected) return {};

if (!associatedFiber) return {};
const currentAssociatedFiber = isCurrentTree(associatedFiber)
? associatedFiber
: (associatedFiber.alternate ?? associatedFiber);
const stateNode = getFirstStateNode(currentAssociatedFiber);
if (!stateNode) return {};
let fiber = knownFiber ?? getNearestFiberFromElement(element);
if (!fiber) return {};

const anotherRes = getParentCompositeFiber(currentAssociatedFiber);
if (!anotherRes) {
return {};
}
let [parentCompositeFiber] = anotherRes;
parentCompositeFiber =
(isCurrentTree(parentCompositeFiber)
? parentCompositeFiber
: parentCompositeFiber.alternate) ?? parentCompositeFiber;
// Get the current associated fiber
fiber = isCurrentTree(fiber) ? fiber : (fiber.alternate ?? fiber);

if (!getFirstStateNode(fiber)) return {};

// Fetch the parent composite fiber efficiently
const parentCompositeFiber = getParentCompositeFiber(fiber)?.[0];
if (!parentCompositeFiber) return {};

return {
parentCompositeFiber,
parentCompositeFiber: isCurrentTree(parentCompositeFiber)
? parentCompositeFiber
: (parentCompositeFiber.alternate ?? parentCompositeFiber),
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const Breadcrumb = ({ selectedElement }: { selectedElement: HTMLElement |
<button
type="button"
title={item.name}
style={{ maxWidth: '120px' }} // CSS hack to force truncation
style={{ maxWidth: '160px' }} // CSS hack to force truncation
className="truncate"
onClick={() => {
handleElementClick(item.element as HTMLElement);
Expand Down
Loading

0 comments on commit 60287ff

Please sign in to comment.