[2.0 rc.15 / next] Docs: say when a ref callback runs (not yet connected), and that ref-attached native listeners run before delegated handlers
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 3/5
- 预计耗时
- 半天
- 新手友好度
- 55/100
- Issue 类型
- 文档
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- javascript, typescript
调研方向
Start with documentation/solid-2.0/07-dom.md, then check the ref examples in MIGRATION.md and CHEATSHEET.md, and the "Root-owned event delegation" section. Run the linked repro (bun run repro) to confirm the ref timing and listener order before writing anything. Done means the docs state when a ref runs relative to connection, rewrite the autofocus example to use onSettled, and note that ref-attached native listeners run before delegated onX handlers and how that affects stopPropagation().
由索引模型根据 Issue 内容生成。
描述
Summary
The 2.0 DOM docs (documentation/solid-2.0/07-dom.md, MIGRATION.md, CHEATSHEET.md) make ref the place for directives and for native listener options. They leave out two timing facts that the 1.x docs stated and that rc.15 still has. Both catch people who follow the 2.0 examples as written.
1. A ref runs before the element is in the document
The 1.x docs say: "The directive functions are called at render time, but are called before the element is added to the DOM." The 2.0 docs never say when a ref runs. They use ref={autofocus} and ref={[autofocus, tooltip({ content: "Save" })]} as headline examples without defining autofocus. The migration guide also says that "element-dependent work rides the apply callback or an effect".
The obvious reading does nothing:
import { onSettled } from "solid-js";
export const log: string[] = [];
// The obvious reading of `ref={autofocus}` from the 2.0 docs.
const autofocus = (el: HTMLElement) => {
log.push(`autofocus ref: isConnected=${el.isConnected}`);
el.focus();
};
export function App() {
let input!: HTMLInputElement;
onSettled(() => {
log.push(`onSettled: input.isConnected=${input.isConnected}`);
});
return (
<>
<input id="naive" ref={autofocus} />
<input id="other" ref={input} />
</>
);
}
Chrome 149, after render():
autofocus ref: isConnected=false
onSettled: input.isConnected=true
document.activeElement: #BODY
Request: state when a ref callback runs: after the element is created and its static attributes are set (#4080 covers the attributes that are not yet set), before its children are inserted, and before it is connected. Show autofocus written so that it works, for example a factory whose owned setup uses onSettled:
const autofocus = () => {
let el: HTMLElement | undefined;
onSettled(() => el?.focus());
return (node: HTMLElement) => void (el = node);
};
2. Native listeners added from a ref run before delegated onX handlers
Delegated handlers run from the render root's listener. A native listener added by a ref therefore runs before every delegated handler for the same event, including a delegated handler on a descendant. Using the on helper from 07-dom.md:
const on = (type: string, handler: (e: Event) => void, options?: AddEventListenerOptions) =>
(el: HTMLElement) => el.addEventListener(type, handler, options);
export const log: string[] = [];
export const App = () => (
<>
{/* a: native listener on a parent, delegated onClick on its child */}
<div id="a" ref={on("click", () => log.push("parent: native listener"))}>
<button id="a-btn" onClick={() => log.push("child: onClick (delegated)")}>a</button>
</div>
{/* b: the same parent listener calls stopPropagation() */}
<div id="b" ref={on("click", (e) => { e.stopPropagation(); log.push("parent: native listener, stopPropagation()"); })}>
<button id="b-btn" onClick={() => log.push("child: onClick (delegated)")}>b</button>
</div>
{/* c: native listener and delegated onClick on one element */}
<button
id="c"
onClick={() => log.push("same element: onClick (delegated)")}
ref={on("click", (e) => { e.stopPropagation(); log.push("same element: native listener, stopPropagation()"); })}
>
c
</button>
</>
);
Clicking #a-btn, #b-btn and #c in turn:
-- click #a-btn
parent: native listener
child: onClick (delegated)
-- click #b-btn
parent: native listener, stopPropagation()
-- click #c
same element: native listener, stopPropagation()
The parent's listener runs before the child's handler. A stopPropagation() in a native listener silences every delegated handler at or below that element, and a delegated handler cannot stop a native listener on an ancestor, because that listener has already run. The 1.x docs had a caveat about stopPropagation() with document-level delegation. Root-owned delegation in 2.0 keeps this behaviour, and the 2.0 docs now present ref={on(...)} next to onClick as the standard pattern without any caveat.
Request: add a short note under "Root-owned event delegation" or next to the on(...) example. It should say that native listeners attached with ref run before delegated handlers, and what that means for stopPropagation().
Versions
solid-js,@solidjs/web,@solidjs/compiler2.0.0-rc.15; the docs as onnextat eb4e019- Case 1: headless Chrome 149. Case 2: happy-dom 21.0.1 (the order follows from where the listeners are attached)
Context
I found these while building a headless UI library on 2.0. Its behaviours are ref factories (focus, outside-press and keyboard listeners, measuring) composed with delegated handlers on the same parts.
Runnable repro: https://gist.github.com/cevr/5d94b73bc4803f440482c266a68280d2 (bun install && bun run repro)
Filed with Claude Code on behalf of @cevr.
- 主要语言
- TypeScript
- 星标
- 36.1k
- 派生
- 1.1k
- 平均合并
- 11 小时 37 分钟
- 30 天内合并 PR
- 344
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
solidjs/solid 的其他 Issue
-
`lazy()`: remounting an already-loaded component renders empty (or the Suspense fallback) since 1.9.14可能已有人在做 @itisrmk 于 2 天前认领。 未关闭1.x
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
[2.0 rc.15 + next] For row lifecycle cleanup inherits the list computation and halts on a shared-state write可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 4/5 3-5 天 新手友好度 51/100
维护者通常 1 天内回复
-
难度 4/5 1-2 天 新手友好度 48/100
维护者通常 1 天内回复
-
难度 4/5 3-5 天 新手友好度 35/100
维护者通常 1 天内回复
-
`createRoot`'s dispose function keeps `fn` alive, so `<For>`/`<Index>` rows retain removed list items可能已有人在做 @boombang 于 4 天前认领。 未关闭1.x
难度 4/5 1-2 天 新手友好度 45/100
维护者通常 1 天内回复
相似的 Issue
-
docs(api): openapi.yaml still documents gif-tools optimize colors as 2-256 instead of 2, 4, 16, 256未关闭area: backend documentation priority: low
难度 2/5 1-3 小时 新手友好度 84/100
snapotter-hq/SnapOtter#2919 ·
维护者通常 1 天内回复
-
accepted :+1: bug db/redis
难度 2/5 1-3 小时 新手友好度 76/100
beekeeper-studio/beekeeper-studio#4882 · 1 条评论 ·
维护者通常 1 天内回复
-
ready-for-triage
难度 2/5 1-3 小时 新手友好度 72/100
konflux-ci/konflux-ui#1630 · 1 条评论 ·
维护者通常 2 天内回复
-
难度 2/5 1-3 小时 新手友好度 67/100
ahmedkhaleel2004/gitdiagram#233 ·
-
难度 2/5 1-3 小时 新手友好度 72/100
simkimsia/netlify-axi#19 ·
维护者通常 1 天内回复