2.0.0-rc.20: createTrackedEffect never re-runs when a dependency is written earlier in the same effect pass

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

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

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

调研方向

The bug is in the reactive core: start with dist/dev.js and the tracked effect's first-run path, where the staged write is propagated before the effect links its dependency, and the staleReader / _reruns logic that does not cover EFFECT_TRACKED nodes. Run the two repro snippets in the issue to confirm the logged values are [0] instead of [0, 1]. Done when both cases log [0, 1] and the existing tracked-effect tests still pass.

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

描述

Describe the bug

A createTrackedEffect whose dependency is written in the same effect pass, before the tracked effect's first run, reads the committed (old) value and never re-runs once the write commits.

Two common shapes:

  1. another effect writes the dependency on its first run;
  2. a parent's onSettled writes a signal passed to the child as a prop.

createEffect(compute, fn) sees the write in both cases (its effect runs again with the new value), so the difference is specific to the tracked effect. A write made by the component's own onSettled, or after the first flush, is seen.

Your Example Website or App

Self-contained repro below (no external link).

Steps to Reproduce the Bug or Issue
import { createEffect, createSignal, createTrackedEffect, flush, onSettled } from 'solid-js'
import { render } from '@solidjs/web'

// 1. Written by another effect on its first run
const log: number[] = []
function C() {
  const [s, setS] = createSignal(0)
  createEffect(() => 1, () => { setS(1) })
  createTrackedEffect(() => { log.push(s()) })
  return null
}
render(() => <C />, document.body)
flush(); await Promise.resolve(); flush()
console.log(log) // [0]  — expected [0, 1]

// 2. Prop written by the parent's onSettled
const log2: number[] = []
function Child(props: { v: number }) {
  createTrackedEffect(() => { log2.push(props.v) })
  return null
}
function Parent() {
  const [s, setS] = createSignal(0)
  onSettled(() => { setS(1) })
  return <Child v={s()} />
}
render(() => <Parent />, document.body)
flush(); await Promise.resolve(); flush()
console.log(log2) // [0]  — expected [0, 1]

Replacing createTrackedEffect(() => log.push(s())) with createEffect(() => s(), (v) => log.push(v)) logs [0, 1] (resp. ends on 1) in both cases.

Expected behavior

The tracked effect re-runs with 1 after the write commits, as createEffect(compute, fn) does.

Screenshots or Videos

No response

Platform
  • solid-js / @solidjs/signals / @solidjs/web: 2.0.0-rc.20 (client build)
  • Reproduced in Chromium (Vitest Browser Mode) and jsdom
  • OS: Linux
Additional context

Reading dist/dev.js: the tracked effect's first run is enqueued on the heap and runs in the user effect queue; the staged write seems to have been propagated to subscribers before the tracked effect linked its dependency, so the commit at the end of the flush notifies nobody. The stale-reader re-derivation (staleReader / _reruns) does not appear to cover EFFECT_TRACKED nodes.

Impact: in an app migrated from 1.x, this silently froze several views on their initial value (a page stuck on "loading", a PDF page indicator, tab state restored from the URL). We worked around it by converting tracked effects to createEffect(compute, fn).

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

环境准备

从这里开始

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

solidjs/solid 的其他 Issue

查看 solidjs/solid 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

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