diff --git a/.changeset/fix-tracked-effect-staged-ref.md b/.changeset/fix-tracked-effect-staged-ref.md new file mode 100644 index 000000000..24b6351d3 --- /dev/null +++ b/.changeset/fix-tracked-effect-staged-ref.md @@ -0,0 +1,5 @@ +--- +"@solidjs/signals": patch +--- + +Re-wake tracked effects after a committed-visibility read observes a staged signal value. diff --git a/packages/web/test/portal-dynamic-ref-3291.spec.tsx b/packages/web/test/portal-dynamic-ref-3291.spec.tsx new file mode 100644 index 000000000..34a631410 --- /dev/null +++ b/packages/web/test/portal-dynamic-ref-3291.spec.tsx @@ -0,0 +1,53 @@ +/** + * @jsxImportSource @solidjs/web + * @vitest-environment jsdom + * + * Repro for https://github.com/solidjs/solid/issues/3291 + * A signal written from a ref callback is never observed by an effect + * when the element is rendered via inside a . + */ +import { describe, expect, test } from "vitest"; +import { createRoot, createSignal, createTrackedEffect, flush } from "solid-js"; +import { Dynamic, Portal, render } from "@solidjs/web"; + +describe("issue #3291: ref signal not observed through in ", () => { + test("repro: effect should see the element written from ref", () => { + const [el, setEl] = createSignal(); + + const observations: boolean[] = []; + let effectRuns = 0; + createTrackedEffect(() => { + effectRuns++; + observations.push(!!el()); + }); + flush(); + + const Wrapper = (props: Record) => ; + + const div = document.createElement("div"); + document.body.appendChild(div); + + const disposer = createRoot(dispose => { + render( + () => ( + + + + ), + div + ); + return dispose; + }); + + // The signal should now hold the element. + const element = el(); + expect(element).toBeInstanceOf(HTMLElement); + + // Regression assertion (#3291): the effect MUST be re-run after the ref + // write, and that run must observe a truthy element. + expect(observations).toContain(true); + expect(effectRuns).toBeGreaterThan(1); + + disposer(); + }); +});