Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

rc.14: inside an action, a non-keyed `<Show>`'s child memo re-runs after its condition turned falsy, and the stale-value throw halts reactivity

已关闭
#4,137 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
47/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
领域
frontend, testing

调研方向

Start by running the linked solid-flow reproduction with bunx vitest run src/core/__tests__/showStaleInAction.test.ts; compare its rc.15 failure with the rc.13 result described in REPRO.md. Trace the update ordering around the non-keyed <Show> branch and its child memo in the reproduction. Done means the child is disposed before it re-runs after when becomes undefined, and the regression test passes without halting reactivity.

由索引模型根据 Issue 内容生成。

描述

Summary

solid-js / @solidjs/[email protected] and rc.15. Regression since rc.13.

Inside an open action, a non-keyed <Show when={lookup[id]}> mounts its branch for a row the action added, and later in the same transaction its condition becomes undefined while the <For> that owns it still lists the id. The engine then re-runs a memo inside the branch (one that reads the Show's narrowed accessor) instead of disposing it first. The read throws "Attempting to access a stale value from that could possibly be undefined", and since that memo has no error boundary the whole system stops: REACTIVITY_HALTED, and no further updates are processed. On rc.13 the same sequence never throws (our DOM test asserts the added row is rendered and selected mid-action).

Traced sequence in the failing case (b is the row the action adds):

step <For> ids <Show when> for b b's branch memo
action adds b, selects it ["a","b"] the row mounts, runs
same transaction, later pass ["a","b"] undefined RE-RUNS, throws the stale read

Two things changed between rc.13 and rc.14 here. A derived keyed record drops a row the action added while the id list still has it; that may be an intended effect of rc.14's held writes (the plain-store writes inside the action are now held with the transaction). The re-run of a child under a falsy non-keyed <Show> is the part that looks wrong regardless: the branch should be disposed before its children run again, and a stale read in a child is turned into a crash of the whole graph.

Reproduction (headless, no DOM, no components)

Not yet reduced to @solidjs/signals alone. Inside the select step, each of its four operations is needed (removing any one avoids the crash: the index-memo read, the selected field write, the nested overlay write, the Object.assign). The surrounding pieces are a plain store over a controlled createOptimisticStore (the flow's own node list), an id list subscribed through Reflect.ownKeys, a membership-cadence index memo read untracked inside a setter, per-row createProjections (keyed mapArray) joined with a plain selection-overlay store, a keyed record projection of those rows, and an action that adds a row, then in a second setter field-writes selected on it, nests a write of the overlay entry (holding the row proxy), and Object.assigns the patch. A signals-only model with those pieces does not reproduce yet: there the added row is visible mid-action, where in the library it is held.

The repro uses solid-flow's headless flow state, rendered through solid-js <For> / <Show> with one eager memo per row. Branch repro/solid-rc14-show-stale (commit ec5977d): solid-flow next at addaf7a with the Solid packages at 2.0.0-rc.15, plus the test and a REPRO.md. Setting the Solid packages back to 2.0.0-rc.13 (steps in REPRO.md) makes the same test pass.

git clone -b repro/solid-rc14-show-stale https://github.com/dsnchz/solid-flow && cd solid-flow
bun install
bunx vitest run src/core/__tests__/showStaleInAction.test.ts   # fails on rc.15: REACTIVITY_HALTED
const [nodes] = createOptimisticStore<Node[]>([node("a", 0)]);
createRoot(() => {
  state = createFlowState({ nodes, edges: [] });
  const view = For({
    get each() { return state.store.visibleNodeIds; },
    children: (id) => Show({
      get when() { return state.nodeLookup.get(id); },
      children: (row) => {
        const label = createMemo(() => `${row().id}:${row().selected}`); // re-runs after `when` went falsy
        return () => label();
      },
    }),
  });
  createRenderEffect(() => JSON.stringify(resolve(view)), () => {});
});
flush();
const addAndSelect = action(function* () {
  state.commands.addNodes(node("b", 200));
  state.commands.updateNode("b", { selected: true });
  yield gate;
});
solid-js / @solidjs/signals result
2.0.0-rc.13 renders; no error
2.0.0-rc.14 REACTIVITY_HALTED (stale <Show> read in the row memo)
2.0.0-rc.15 REACTIVITY_HALTED (stale <Show> read in the row memo)

On rc.15, without the updateNode call (add only), or selecting an existing row, or updating the new row's data instead of selected, nothing throws.

Where we hit it

@dschz/solid-flow composes flow commands with createOptimisticStore + action (the composition from #3085; our pin for #3665 covers this same add-then-select-in-an-action scenario). In the real component, NodeWrapper's culling memo reads the row through the Show's accessor, so a user action that adds a node and selects it (addNodes then updateNode(id, { selected: true })) before its yield freezes the whole flow on rc.14 and rc.15. We are holding our rc.15 bump on it.

主要语言
TypeScript
星标
36.1k
派生
1.1k
平均合并
11 小时 37 分钟
30 天内合并 PR
344

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

solidjs/solid 的其他 Issue

查看 solidjs/solid 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。