Skip to content

Dependency leakage between HTML attributes (=Extra dependencies causing unnecessary re-runs) #2525

@nns2009

Description

@nns2009

Describe the bug

I found that classList (and possibly other HTML attributes) causes unnecessary re-runs. In the following code, createEffect only reruns when v() changes (I manually examined w.vs). Similarly, HTML text re-runs only when v() changes (I only get one render for ${i} for the element, which was updated. Yet, every single change of any element (values()[i]) I get classList for ${i} for every single element in values(). This is totally unexpected, especially considering that classList and HTML renders are right next to each other.

import { createEffect, createMemo, createSignal, Index } from "solid-js";

export default function ClassListRerunExperiment() {
	const names = createMemo(() => ['one', 'two', 'three', 'four', 'five', 'six', 'seven']);
	const [values, setValues] = createSignal([1, 2, 3, 4, 5, 6, 7]);

	function setOneValue(i: number, v: number) {
		const next = [...values()];
		next[i] = v;
		setValues(next);
	}

	return <Index each={names()}>{(t, i) => {
		console.log('Row render:', i);
		const v = createMemo(() => values()[i]);

		createEffect(() => {
			if (i !== 0) return;
			const w = window as any;
			w.vs = w.vs ?? [];
			w.vs.push(v());
		});

		return <div classList={{
			'big': (console.log(`classList for ${i}`), v() >= 5),
		}}>
			{(console.log(`render for ${i}`), v(), 'z')}
			<button onClick={() => setOneValue(i, v() - 1)}>-</button>
			{v()}
			<button onClick={() => setOneValue(i, v() + 1)}>+</button>
			<button disabled={values()[i] <= 2}>Low</button>
			{values().join(',')} {values()[i]}
		</div>
	}}</Index>;
}

I was able to track down the problem to this specific line:

<button disabled={values()[i] <= 2}>Low</button>

With this specific line removed, reactivity works as expected, any '-'/'+' press only results in a single classList for ${i} log. Somehow, using values() in 'disabled' attribute (maybe also any other HTML attribute?) seems to leak values() as a dependency of classList. At the same time, using values() for rendering:

{values().join(',')}

does not cause any leakage.

Your Example Website or App

https://stackblitz.com/edit/solidjs-dependency-leakage-zqkzgank?file=src%2FApp.tsx

Steps to Reproduce the Bug or Issue

Open the link, open DevTools console.
Click any of the '-' or '+' buttons.

Expected behavior

Only one classList for ${i} is logged exactly for the element being updated

Screenshots or Videos

No response

Platform

  • OS: Windows, also Stackblitz, most likely cross-platform
  • Browser: Edge, Opera, Yandex Browser
  • Version: All latest

Additional context

No response

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