Infinite rendering loop when nesting render props
Describe the bug
I was experimenting with rendering props and found a strange behaviour.
When nesting a component with a render prop inside the <Show /> component, Solid goes into an infinite rendering loop.
I've prepared a minimal Playground demo demostrating the problem.
The issue seems to appear only when using <Show /> with a function instead of a direct component.
From the playground, this works as expected:
<Show when={showNoLoopAlt()}>
<WithRandom>{(data) => <span>{data}</span>}</WithRandom>
</Show>
while this doesn't:
<Show when={showLoop()}>
{() => <WithRandom>{(data) => <span>{data}</span>}</WithRandom>}
</Show>
Also, rearranging the code to make <Show /> the child component doesn't present the same problem:
<WithRandom>
{(data) => (
<Show when={showNoLoop()}>{() => <span>{data}</span>}</Show>
)}
</WithRandom>
Your Example Website or App
https://playground.solidjs.com/?version=1.4.1#NobwRAdghgtgpmAXGGUCWEwBowBcCeADgsrgM4Ae2YZA9gK4BOAxiWGjIbY7gAQi9GcCABM4jXgF9eAM0a0YvADo1aAGzQiAtACsyAegDucAEYqA3EogcuPfld69mQqLjgBlNAHNoarA6cXNwBhBUJ6NxF-CEdaCGC1OCgIekJoxwBBZlYyOkZ03gApdwANAvcAC1pDAriAWQYIXGjpOQVlVQ1tPQsrKxl6CGZcNDjeejI4ACVkkQUACgBKexinOLpEgDo1Wi95lQAFKYBRLTqAeQBVADkAFRVFy1X6xtx5pd4AXgA+NYgNuDbXb7MAXG73MCLR4BOIJJIpQjvZY-P4AoF7FTBAAyxwy10uBwe0IgAWY6z4wAAblA1PQ4FheJNcAA1Gl0gC6X0CSTcnh8NPmdVcFU2jFmCyhT0czh5cFCnAicBESK+vxAAUcTNZtLgguFovFMCWxMckmJASEuCYMWpOt4UDIvCyOTyAB4UjATOJvk9JH0IAMhiMxgB1NC4CozUQLQjyQhkRD8JwVNBqERCCCJ+YiVxQRMer2MZG-YolTbHRLwJpSZbq1Zk-58W10rkTaaGpZSwRwK2MGKu74gWO0eObZgptMZ+bN3VQySu-Q+qx+kkBwbDUYxDKERG10nk3jAMhVQzXWhY2gjhlMyrVM8Xkecz7c1weby+eYyGmTE2oinHu9z0vQgMjUZpGR7W9TyAkdQNwJ8X15d8BS-NQfy7BsyH-E8HzSCDcCg3CEJlV8+Q-VD0ItHtrV4fZVkcAcNUcXhXRENBKW+JjmJYkwIlwMY2LIKATESERviw1w0GYBdeNwfiIE4+juJYsMIyjOYYEU5TlJAbNc2LFZtO0y0aNdMhCGSQcc1wKB530czLK7IypBXZyF1UyNDS05SFzYjiuJYvzvO011ZPk3hYQ0ZgAGtPl0gybxPe9gPmABCADoNw41JGCoyQAy5KRw+AB+DoUzEWiIFoXgdhHRYVF4RMVAyyrqtqwh6rAVyjJkvi4ly7jXQ89SFAGnS9Jsgy6OcwaoN4QwKmEOKCpg3ccvi1UWIchSQGs2yF22747KgsbuMWbqQv0YavIC3z2LG1j7oChiwrGSKpNijaUUSwDcLgtKVr+sDstO5j8qS1b-uWUqVHKuBWpq4CGRpXBOsajoWvmKrEave0wM6i6fP0V6FOeli5oWpbwd+4CoZysmGOu6NNN0vaDLMiydr2uzDrspmNNBhcTtu-QgtusWlMGkmIviKLPo+b7IJwlL0uVoq51BxxqcMLLobKzR4fmdq0aamgT1o42VEJwbib60nJeY10KcWiBlrVtbNf4BXfiG8NPOZwcJqgdnDt23Mec5o73L9kbNOtx39GFh27v8pSF284kVysDMxEYFUUVdbdCF4RcGTmZh6CrXBNi8HsKzgKuACF8AASWVFQoB3B5UuJLr2SAA
Steps to Reproduce the Bug or Issue
- Open the Playground demo
- Click the
show (loop)button - Watch the console
Expected behavior
No infinite loop.
Screenshots or Videos
No response
Platform
- OS: Linux
- Browser: Chrome
- Version: 103.0.5060.53
Additional context
No response
Yeah it's because we have to support:
<Show when={something()}>{something()}</Show>
more specifically
<Show when={something()}><Dynamic /></Show>
Where the component returns a memo.
There is basically no difference from runtime perspective between that and what you wrote for infinite loop
createComponent(Show, {
get when() { return something() }
get children() { return createComponent(.....)// that returns a function }
}
createComponent(Show, {
get when() { return something() }
children: () => createComponent(.....)
}
I did try at one point to look at if it were a getter or not but that also has holes. So sort of stuck between supporting argument functions and not supporting:
<Show when={something()}>{something}</Show>
Where something is a function.
Also worth looking at this older issue: https://github.com/solidjs/solid/issues/869
Thanks for taking the time!
Tinkering with the playground code I also tried to use different hooks other than createComputed, but with the same result.
Interestingly if the child component does not use any hook other than createSignal the problem seems to disappear.
It's just a matter of being a tracking scope being present. I'd just avoid sending argument callbacks to <Show> at the present. It's a quirk of Show and Switch/Match. We might find a way to mitigate this at some point but that's why we are always careful to untrack things automatically because without that sort of control zone guarantee things can get out of hand quickly.
I think the createComputed is just want causing the chain to happen but the over-tracking is already present at that point.
Ok, I think I get what you're saying about the tracking.
If it's fine with you, I'd leave this issue open considering this has some footgun potential.
Thanks for now!
I don't think there is an easy solve. I was thinking the presence of a getter could make this an easier determination. But passing props through would be a getter. So this is unlikely to change.
<Show when={something()}>{props.renderItem}</Show>
This is ambiguous. Basically render props make it so we can't detect the difference.
It seems like in recent versions of solid the problem disappeared. I guess the key change was introduced in 1.5.0 with the new batching behaviour.
I just tried to replicate the original example and it looks fine now (I had to rewrite it because the playground link is not working anymore).