solid icon indicating copy to clipboard operation
solid copied to clipboard

Infinite rendering loop when nesting render props

Open cdellacqua opened this issue 3 years ago • 5 comments

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

  1. Open the Playground demo
  2. Click the show (loop) button
  3. 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

cdellacqua avatar Aug 08 '22 17:08 cdellacqua

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

ryansolid avatar Aug 08 '22 18:08 ryansolid

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.

cdellacqua avatar Aug 08 '22 19:08 cdellacqua

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.

ryansolid avatar Aug 08 '22 19:08 ryansolid

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!

cdellacqua avatar Aug 08 '22 19:08 cdellacqua

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.

ryansolid avatar Aug 08 '22 21:08 ryansolid

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).

cdellacqua avatar Jan 03 '23 22:01 cdellacqua