AwsmBuff
AwsmBuff17mo ago

Signal Arrays do not work with HTML-Elements?

I tried to debug my problem for a while until I realized that this works:
import { useSignal } from "@preact/signals";

export default function Island() {
const activated = useSignal(false);
return (
<button
onClick={() => {
activated.value = !activated.value;
}}
class={activated.value ? "bg-blue-800" : "bg-red-800"}
>
Test
</button>
);
}
import { useSignal } from "@preact/signals";

export default function Island() {
const activated = useSignal(false);
return (
<button
onClick={() => {
activated.value = !activated.value;
}}
class={activated.value ? "bg-blue-800" : "bg-red-800"}
>
Test
</button>
);
}
but this does not:
import { useSignal } from "@preact/signals";

export default function Island() {
const activated = useSignal([false]);
return (
<button
onClick={() => {
activated.value[0] = !activated.value[0];
}}
class={activated.value[0] ? "bg-blue-800" : "bg-red-800"}
>
Test
</button>
);
}
import { useSignal } from "@preact/signals";

export default function Island() {
const activated = useSignal([false]);
return (
<button
onClick={() => {
activated.value[0] = !activated.value[0];
}}
class={activated.value[0] ? "bg-blue-800" : "bg-red-800"}
>
Test
</button>
);
}
10 Replies
AwsmBuff
AwsmBuffOP17mo ago
This is just an example but in my project: Being able to use an array at that point instead of hardcoding all the signals would help me a lot. Am I using it wrong?
ioB
ioB17mo ago
You need to make sure to set the actual signal value alternatively, you could have an array of signals
AwsmBuff
AwsmBuffOP17mo ago
At first: Thank you for your help! What do you mean by that? I thought I am setting the signal value with activated.value[0] = !activated.value[0];. At least that is what I am doing in the first example and there it works. Yea, that is what I am now trying to do but it gets ugly in my case and I wondered if an array inside of a signal would work too.
ioB
ioB17mo ago
preact signals don’t do deep observability. You can do what you’re doing right now, but you need to reassign the array afterwards. activated.value = activated.value should work it’s similar to svelte state if you’ve ever done that before
AwsmBuff
AwsmBuffOP17mo ago
Reassign like right after negating the value? (Just asking because I tried that and it did not work.) Doesn't sound familiar to me 😅
ioB
ioB17mo ago
marvin probably knows better than me 😅
marvinh.
marvinh.17mo ago
yup, like lino said: Signals track reactivity through assignment. That means a signal will only update when you do signal.value = whatever . Pushing into an array or mutating the contents of the array is not tracked. Assigning to an index in the array assigns to the array, not the signal. So it's not tracked. We thought about doing deep tracking like this when creating signals, but ultimately you always end up having to Proxy every collection type in JS like Array, Map, Set,... which gets a bit nasty with edge cases. Add on top that it blurs the line between when something is tracked and when it's not. So we opted for the simpler model. @lino-levan nah you're already spot on. Just providing a little more historic context 😄
AwsmBuff
AwsmBuffOP17mo ago
Thank you very much for the detailed explanation. Would you recommend to do an array of signals instead a signal containing an array then too or is there a better way?
marvinh.
marvinh.17mo ago
Depends on what parts of the data you want to react to in your UI. If you only ever deal with the full array you can just clone it and assign it to update it.
const next = activated.value.slice(); // clone old array
next[0] = !activated.value[0]; // update value
activated.value = next; // trigger signals update
const next = activated.value.slice(); // clone old array
next[0] = !activated.value[0]; // update value
activated.value = next; // trigger signals update
some devs also may use destructuring to clone arrays. Both work the same way
const next = [...activated.value]
const next = [...activated.value]
AwsmBuff
AwsmBuffOP17mo ago
Cool, thanks again for the suggestions! And @lino-levan for the first aid! Hope this will help others in the future as well.