Skip to content

Derived is still not updating synchronously #15721

@mcous

Description

@mcous

Describe the bug

I believe this issue is a continuation of #15590. I was excited to see the fix for this issue, and immediately updated so I could remove some workarounds I had in place for my test suite. To my surprise, removing the workarounds caused the tests to fail.

I have JS module that takes a signal as input, feeds that signal into a mutable $derived internally, and exposes an onChange method to optimistically update the $derived. The module and tests look something like this:

export type AppStateOptions = Readonly<{
  source: () => string;
}>;

export interface AppState {
  value: string;
  onChange: (nextValue: string) => void;
}

export const createAppState = (
  options: AppStateOptions
): AppState => {
  const source = $derived(options.source());
  let value = $derived(source);

  const onChange = (nextValue: string) => {
    value = nextValue;
  };

  return {
    get value() {
      return value;
    },
    onChange,
  };
};
import { test, expect } from "vitest";

import * as Subject from "../app-state.svelte";

test("optimistically updates value", () => {
  const result = Subject.createAppState({ source: () => "wrong" });

  result.onChange("right");

  expect(result.value).toBe("right");
});

This test fails!

 FAIL  src/lib/__tests__/app-state.svelte.spec.ts > optimistically updates value
AssertionError: expected 'wrong' to be 'right' // Object.is equality

Expected: "right"
Received: "wrong"

Just like in #15590, if I read result.value before calling onChange. The test passes.

+ void result.value;
  result.onChange("right");

  expect(result.value).toBe("right");

Interestingly, removing the intermediate const source = $derived(options.source()) also fixes the issue:

- const source = $derived(options.source());
- let value = $derived(source);
+ let value = $derived(options.source());

At this point, this is mostly an annoyance in testing. Outside of this particular issue, though, we've been having persistent issues with $derived chains failing to update when expected. These issues have proved difficult to minimally reproduce, but I will file more issues if/when I'm able to boil them down

Reproduction

This was a bit tricky to reproduce in the REPL - it does not repro in the <script> tag of a component. I was, however, able to repro by placing the code in question inside a button's click handler:

https://svelte.dev/playground/c88b99a5f9574364a587e1211a0b230a?version=5.25.10#H4sIAAAAAAAAE3VSXYucQBD8K81woAtG390POHL5BYG8xMDNaq83d2PPMNN6BvG_h9lZvRU2T0pVdRVWOQmSHYpSvKBTAzZAhqHrWZ41ikxclEYvyt-T4L82yAIgsuXo2drcD6g5YGfp8RFeG2Ik9qIUhwiWxrIy5MH1hB6KU0UVHXztlOXwzqqzxjFMtUPJ-GztT5aMM1yc6SDJC2ntNx-gW0r-7pPgUXFtyDMYqrWqP-AI6Q6OJ5gCtZDXQzjC1jxNPp2hNtlFaVFAT7XpOiQGNnBR40oMRjXRJR-k7jESETD0_U1Si2niVPvGi11INhpzbdr09cdosWZsoBJXUSUyaA3D03RnOr9eT-eKDsVXM3Q498yGYLp94nxyPQGj50MRqZPIBOPIomTX45z9Z7oHFW43fCy4GxPH60ix1G2ZoXhFrKT-Fb7lawONDN70rg6Spyb-cxtpRQBBdS3hXhTPdvvQAsCyc2w75BGOvEkDgNVlJfcBn28mDrl3tGjbJTXdLdAqGdZbgDmLzyU8Wyzn_bb6P5lgqfSnokaUF6k9zv8A1r21IW8DAAA=

System Info

System:
    OS: macOS 14.3.1
    CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    Memory: 575.77 MB / 32.00 GB
    Shell: 5.9 - /usr/local/bin/zsh
  Binaries:
    Node: 22.14.0 - /usr/local/bin/node
    npm: 10.9.2 - /usr/local/bin/npm
    pnpm: 9.15.5 - ~/.nvs/default/bin/pnpm
  Browsers:
    Chrome: 123.0.6312.86
    Safari: 17.3.1
  npmPackages:
    svelte: latest => 5.25.10

Severity

annoyance

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions