Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature suggestion: linked roots in devtools #477

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/adapter/10/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,25 @@ import { ComponentHooks, HookState, PreactBindings } from "../shared/bindings";
import { RendererConfig } from "../shared/renderer";
import { getRenderReasonPost } from "./renderReason";

function getVirtualParent(vnode: VNode): VNode | null {
const vnodeChildren = getActualChildren(vnode);
if (vnodeChildren.length > 0 && isVNode(vnodeChildren[0])) {
const child = vnodeChildren[0];
if ((child as any).__linked_parent) {
return (child as any).__linked_parent;
}
}
return null;
}

// Mangle accessors

/**
* Get the direct parent of a `vnode`
*/
export function getVNodeParent(vnode: VNode): VNode | null {
return (
getVirtualParent(vnode) ||
(vnode as IVNode)._parent ||
(vnode as any).__ ||
// Older Preact X versions used `__p`
Expand Down Expand Up @@ -172,7 +184,9 @@ export function getHookState(
export function getActualChildren(
vnode: VNode,
): Array<VNode | null | undefined> {
return (vnode as IVNode)._children || (vnode as any).__k || [];
const children = (vnode as IVNode)._children || (vnode as any).__k || [];

return [...children, ...((vnode as any).__linked_children || [])];
}

// End Mangle accessors
Expand Down
23 changes: 21 additions & 2 deletions src/adapter/11/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ export function getDisplayName(internal: Internal, config: RendererConfig) {
export function getActualChildren(
internal: Internal,
): Array<Internal | null | undefined> {
return (internal as Internal)._children || (internal as any).__k || [];
const children =
(internal as Internal)._children || (internal as any).__k || [];

return [...children, ...((internal as any).__linked_children || [])];
}

export function getComponent(node: HookState | Internal): Component | null {
Expand All @@ -192,11 +195,27 @@ export function getDom(internal: Internal): HTMLElement | Text | null {
return (internal as Internal)._dom || (internal as any).__e || null;
}

function getVirtualParent(vnode: Internal): Internal | null {
const vnodeChildren = getActualChildren(vnode);
if (vnodeChildren.length > 0 && isInternal(vnodeChildren[0])) {
const child = vnodeChildren[0];
if ((child as any).__linked_parent) {
return (child as any).__linked_parent;
}
}
return null;
}

/**
* Get the direct parent of a `vnode`
*/
export function getVNodeParent(internal: Internal): Internal | null {
return (internal as Internal)._parent || (internal as any).__ || null;
return (
getVirtualParent(internal) ||
(internal as Internal)._parent ||
(internal as any).__ ||
null
);
}

/**
Expand Down
9 changes: 9 additions & 0 deletions src/adapter/shared/traverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ export function shouldFilter<T extends SharedVNode>(
) {
return true;
}
// This is something like: when using linked parents / children, they come from
// separate commits, but they are linked, so will have children / parents set.
// Removing this results in duplicates on first start (interestingly, not
// duplicates when you toggle the "Fragments" element filter. I think this is
// because it then recreates the tree from children, and doesn't have the
// problem of creating this from separate commits)
if ((vnode as any).__linked_parent && !bindings.getComponent(vnode)) {
return true;
}

return false;
}
Expand Down
51 changes: 51 additions & 0 deletions test-e2e/fixtures/apps/root-multi-nested.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { h, render } from "preact";
import { useEffect, useState } from "preact/hooks";

function Display(props) {
return <div data-testid="result">Counter: {props.value}</div>;
}

function Child(props) {
return <h3>Child {props.id}</h3>;
}

function linkRoots(parent, child) {
// v10 linking
parent.__linked_children = [child];
child.__linked_parent = parent;
}

function Counter({ id }) {
const [v, set] = useState(0);

const ChildRoot1 = () => <div id="child-1" dangerouslySetInnerHTML="" />;
const ChildRoot2 = () => <div id="child-2" dangerouslySetInnerHTML="" />;

const childRoot1Vnode = <ChildRoot1 />;
const childRoot2Vnode = <ChildRoot2 />;

useEffect(() => {
const child1 = <Child id="1" />;
const child2 = <Child id="2" />;

linkRoots(childRoot1Vnode, child1);
linkRoots(childRoot2Vnode, child2);

render(child1, document.getElementById("child-1"));
render(child2, document.getElementById("child-2"));
}, []);

return (
<div style="padding: 2rem;">
<h3>{id}</h3>
<Display value={v} />
<button onClick={() => set(v + 1)}>Increment</button>

{childRoot1Vnode}
{childRoot2Vnode}
</div>
);
}
const app1 = <Counter id="Root" />;

render(app1, document.getElementById("app"));