Skip to content

$derived does not update on attribute addition #15911

Open
@jerrythomas

Description

@jerrythomas

Describe the bug

Something seems to have changed recently in the way $derived works.
This test below used to update the lookup on addition of children attribute.

it.only('should handle a nested array', () => {
		const items = $state([{ text: 'A' }, { text: 'B' }, { text: 'C' }])
		const lookup = $derived(deriveLookupWithProxy(items))
		const result = () => lookup
		expect(Array.from(result().keys())).toEqual(['0', '1', '2'])

		const addChildren = () => {
			items[0].children = [{ text: 'A1' }, { text: 'A2' }]
		}
		addChildren()
		// flushSync()
		expect(Array.from(result().keys())).toEqual(['0', '0-0', '0-1', '1', '2'])
	})

I still see that the function is called on the change, but it does not seem to update the values. This used to work recently.

Reproduction

import { describe, it, expect } from 'vitest'
import { isNil, has } from 'ramda'
// import { flushSync } from 'svelte'

const defaultFields = { children: 'children' }

class Proxy {
	constructor(value, fields) {
		this.fields = fields
		this.value = value
	}
	get(fieldName, defaultValue) {
		return this.has(fieldName) ? this.value[this.fields[fieldName]] : defaultValue
	}
	has(fieldName) {
		const mappedField = this.fields[fieldName]
		return !isNil(mappedField) && has(mappedField, this.value)
	}

	get hasChildren() {
		return this.has('children') && Array.isArray(this.value[this.fields.children])
	}
}

export function deriveLookupWithProxy(items, fields = defaultFields, path = []) {
	const lookup = new Map()
	console.log($state.snapshot(items))

	items.forEach((item, index) => {
		const itemPath = [...path, index]
		const key = itemPath.join('-')
		const proxy = new Proxy(item, fields)

		lookup.set(key, proxy)

		if (proxy.hasChildren) {
			const childLookup = deriveLookupWithProxy(proxy.get('children'), fields, itemPath)
			for (const [childKey, childValue] of childLookup.entries()) {
				lookup.set(childKey, childValue)
			}
		}
	})
	return lookup
}

describe('deriveLookupWithProxy', () => {
	it.only('should handle a nested array', () => {
		const items = $state([{ text: 'A' }, { text: 'B' }, { text: 'C' }])
		const lookup = $derived(deriveLookupWithProxy(items))
		const result = () => lookup
		expect(Array.from(result().keys())).toEqual(['0', '1', '2'])

		const addChildren = () => {
			items[0].children = [{ text: 'A1' }, { text: 'A2' }]
		}
		addChildren()
		// flushSync()
		expect(Array.from(result().keys())).toEqual(['0', '0-0', '0-1', '1', '2'])
	})
})

Logs

System Info

System:
    OS: macOS 15.4.1
    CPU: (16) arm64 Apple M4 Max
    Memory: 640.27 MB / 48.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 23.4.0 - ~/.nvm/versions/node/v23.4.0/bin/node
    npm: 10.9.2 - ~/.nvm/versions/node/v23.4.0/bin/npm
    pnpm: 10.7.0 - ~/Library/pnpm/pnpm
    bun: 1.2.12 - /opt/homebrew/bin/bun
  Browsers:
    Chrome: 136.0.7103.93
    Safari: 18.4

Severity

blocking an upgrade

Metadata

Metadata

Assignees

No one assigned

    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