-
Notifications
You must be signed in to change notification settings - Fork 789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug: Conditional slot rendering removes always the last slot element, using key is a workaround #5263
Comments
Hey @michaelworm 👋 I confirmed the issue you were seeing, but think we may already have a solution in the works! I installed a recent dev build of Stencil and it appears to solve the issue you're seeing. If you want, you can try it out by installing In the meantime, I'll get this issue labeled and tied back to the relevant PR. Thanks for letting us know! |
This adds a new transformer, `performAutomaticKeyInsertion`, which will add `key` attributes into certain JSX nodes in the `render()` method of a Stencil component. This will allow the Stencil runtime to make more accurate decisions about when to create and destroy child nodes during re-rendering, especially in circumstances where nodes are changing order and so on. There are some limitations on when we can safely insert keys without possibly introducing incorrect behavior. In particular, we don't want to insert keys in the following situations: - when a `render` method has more than one return statement - when a `render` method has a conditional (ternary) expression in it - when a JSX node is located inside of a JSX expression (i.e. within curly braces like `<div>{ ... }</div>`) In each of these cases we don't have the static analysis chops to know when a given JSX syntax tree node is supposed to correspond to the same HTML element at runtime, whereas in the 'single return, JSX children case' like: ```tsx class Component { render () { return <div><img /></div> } } ``` We know without a doubt in this case that the `div` and `img` JSX nodes are always supposed to correspond to the same HTML elements at runtime, so it's no problem to add keys to them. The key insertion transformer also does not transform JSX nodes which are not part of Stencil components, so if a project had, for some reason, a React component or something in it we will leave it alone. fixes #1968 fixes #5263 STENCIL-893
…5143) * test(slot): add karma test for #1968 this commit adds a karma test for #1968, based on the minimal reproduction case that we received for said issue * feat(runtime): automatically insert `key` attrs during compilation This adds a new transformer, `performAutomaticKeyInsertion`, which will add `key` attributes into certain JSX nodes in the `render()` method of a Stencil component. This will allow the Stencil runtime to make more accurate decisions about when to create and destroy child nodes during re-rendering, especially in circumstances where nodes are changing order and so on. There are some limitations on when we can safely insert keys without possibly introducing incorrect behavior. In particular, we don't want to insert keys in the following situations: - when a `render` method has more than one return statement - when a `render` method has a conditional (ternary) expression in it - when a JSX node is located inside of a JSX expression (i.e. within curly braces like `<div>{ ... }</div>`) In each of these cases we don't have the static analysis chops to know when a given JSX syntax tree node is supposed to correspond to the same HTML element at runtime, whereas in the 'single return, JSX children case' like: ```tsx class Component { render () { return <div><img /></div> } } ``` We know without a doubt in this case that the `div` and `img` JSX nodes are always supposed to correspond to the same HTML elements at runtime, so it's no problem to add keys to them. The key insertion transformer also does not transform JSX nodes which are not part of Stencil components, so if a project had, for some reason, a React component or something in it we will leave it alone. fixes #1968 fixes #5263 STENCIL-893 --------- Co-authored-by: Ryan Waskiewicz <[email protected]>
The fix for this issue has gone out in today's v4.12.0 release |
@rwaskiewicz Awesome, thank you! |
@rwaskiewicz I can still reproduce the bug on 4.12.0 release when removing the manually added keys (footer slot is also gone when deleting header slot) => https://stackblitz.com/edit/node-wacxnr?file=src%2Fcomponents%2Fmy-comp.tsx Is there something I am missing here? |
Hey @michaelworm, thanks for checking back in to see about this issue! I just took at look at that stackblitz repro and I'm seeing something a bit puzzling - I'm only able to reproduce the issue immediately after doing a full-page refresh of the stackblitz environment, and if I refresh the stackblitz 'browser-within-a-browser' I can than no longer reproduce the issue. I'm using this link: https://stackblitz.com/edit/node-wacxnr?file=src%2Fcomponents%2Fmy-comp.tsx Here's what I see: Screen.Recording.2024-02-01.at.9.49.35.AM.movto sort of narrate that, I'm loading up the link and once the stackblitz env boots and starts running the project I am initially able to reproduce the issue. However, if I then click the refresh button for the little stackblitz browser it won't reproduce any more. This made me think there might be some caching behavior on stackblitz' side which is contributing to this, so I copied your component code and So all that said, could you possibly take a look at putting together a local reproduction case? You can create a Stencil project quickly with |
@alicewriteswrongs Yes sure, I will try and provide a local example asap. In the meantime, do you have any idea why the issue never occurs when manually adding keys to the slots (as it is in the first demo on Stackblitz)? Like this: return (
<div>
{hasHeader && <slot key="header" name="header" onSlotchange={() => forceUpdate(this.host)} />}
<slot />
{hasFooter && <slot key="footer" name="footer" onSlotchange={() => forceUpdate(this.host)} />}
</div>
); |
Yeah, I would expect that in versions of Stencil prior to 4.12.0 adding keys there should fix the issue because the keys will allow the Stencil runtime to do a more accurate job of matching up children across component re-renders, so that the footer won't get dropped when another child disappears. In 4.12.0 we added automatic insertion of randomly-generated keys into JSX nodes where it is safe to do so - in this case, that would be into the un-named |
@alicewriteswrongs Actually I could not reproduce it locally, you are right. So it must be something with Stackblitz. Thank you for the support! |
Oh good, glad we were able to get to the bottom of it! I suspect it's something in the caching that stackblitz does to get their environment to boot so rapidly in the browser, but who knows 🤷♀️ good luck with your project! |
Prerequisites
Stencil Version
We were using 4.8.2 but it also happens with 4.10.0
Current Behavior
It occurs that always the last slot element will get removed in shadow dom when conditionally rendering the elements and removing the source slot element in the light dom.
experimentalSlotFixes
makes no difference but using the "key" prop with a unique name fixes the issue.Consider the following basic example below.
Expected Behavior
The Header Slot shall be removed in the example but the Footer Slot should be kept.
System Info
Steps to Reproduce
When clicking on "Remove Header" the Footer Slot will get removed and the Header Slot will be empty.
It seems that when adding elements between the slots, the bug does not occur.
Code Reproduction URL
https://stackblitz.com/edit/node-dhtsl3?file=package.json,src%2Fcomponents%2Fmy-comp.tsx
Additional Information
Remove the "key" props in the StackBlitz Demo to see the issue.
The text was updated successfully, but these errors were encountered: