Slot forwarding with render functions #10847
Replies: 4 comments 6 replies
-
I've been giving this some thought, but I still haven't really come up with an approach I'm happy with. If you're exclusively using render functions, then perhaps you could switch to using a props to pass down the slots, rather than using actual slots? Many of the usual benefits of true slots don't really apply if you're just using render functions. Another approach might be to use the The But I'm not really happy with that approach. It works OK in this example, but it could block updates if the I feel like there's a trick here I'm still missing, but I figured I'd share those ideas for now, maybe they'll work in your specific use case. |
Beta Was this translation helpful? Give feedback.
-
I've been doing this (and avoiding re-renders) passing down the base compo as a prop and capturing slots in a computed or directly on the render function if any (on whatever deeply nested child compo). Let's say you send a How you actually end up doing this (passing down this "context", traversing |
Beta Was this translation helpful? Give feedback.
-
It's really no different (passing down a compo instance as a prop, security-wise or whatever) than traversing v-nodes upwards through Let's say for instance you create an input component with complex rules, and then you create a form component that checks the state of those rules on any compo that supports it before submitting, otherwise it disables submit...how would you "hook" those components to work together no matter how deeply nested the input compo is inside the form compo ? Whatever the answer, is the way you will also pass down (in whichever format of your preference) the slots objects from the original compo to "inject" (or even cascaded through every single parent compo not just the "base" one). now, specifically (and probably with syntax errors), I proppossed something like this: base compo:
(of course, just for the sake of it, I'm using templates here...) child compo render function, should include something like this:
Couple of notes...
That's how I would (and do) deal with passing down slots, again: just avoid using/passing-down those slots as slots on each compo on the whole chain of compos, or you'll get those nasty re-renders, never include them in any manner in any compo except the one that is intended to render them, and for that you need to hook/communicate/whatever compos some way, which leads us to the main point here... How exactly are you going to connect those compos so that it can directly access an object of slots? (which in the end, are nothing more than functions themselves), here's a couple of ways...
Provide/Inject is probably the more "elegant" way, but seriously, I don't see the problem with just passing down a (so called) context. Hope that clarified the idea a little. EDIT: very important note, just noticed I actually gave you an example (for the child-compo render function) of a transparent wrapper renderer, intended to actually render another component (a component that renders components with the final slots, scopes, etc) and that's also why I'm still passing down the But in your case, if you do have control of that render function, then just ignore everything after the |
Beta Was this translation helpful? Give feedback.
-
Ok...with all due respect, this is all wrong XD First off your problem is not on passing down slots but reactivity inside your render functions triggering re-renders and slots react/get-called-again when their source compo is re-rendered. Just put a In your example A (App.vue) is getting re-rendered even when there's no B or C child compos.
There you go...if you check your console now, you'll see you'll get re-renders on Follow and Text compos, but not anymore on App, which was the only unnecesary re-render there. So...why does Follow get re-rendered as well ?Simple, because left/top changes inside the render function trigger the re-render (and it's the intended behavior in your example, or it wouldn't update the style prop) So...why does Text get re-rendered as well ?Because you're passing down a slot that belongs to a component that is being re-rendered so it triggers reactivity on the slot and thus re-renders the child compo using that reactive (this is actually out of my head, not a proper documented answer, an excercise of imagination or an actual Vue thing wether intended or unintended?...who knows...). Go back to my previous comment, "hook" your components in whichever way you find best fits your needs and instead of "sending slots down from the parent", "go up from your child compo searching for slots". First off, on Follow.js, remove the slots param for then, on Text.js...
Then just use And there you go...you can check your console and see that only "Follow" is being re-rendered on mouse move. Again, this approach (actually, how you look for the slots you need) depends a lot on the specific structure of the virtual dom, so it all comes down to what you're trying to achieve, but keep in mind what I said on my previous post about "explodable slot names that follow some logic of some sort", writing a function that looks for those (potentially, inject it from the top parent that "supports inheritable/injectable slots"), etc etc etc. A personal comment on this...When developing that transparent wrapper/renderer that I mentioned on my previous post, I think I remember trying to "send down" slots through props and still got the same problem of unnecesary re-renders (which would make sense for the reason I described above about reactives on the render function). When checking this kind of stuff, just make sure you're console logging renders, don't rely on something like a non-reactive timestamp. On a different subject...so why does the templates version work propperly ?Simply put: it doesn't XD So why doesn't the timestamp get constantly updated ?Simply because on that implementation you're passing it down as And now for something completelly different...Nah, just kidding ñ_n Cheers ♥ |
Beta Was this translation helpful? Give feedback.
-
For a few years now, we have been writing our Vue components using render functions rather than Vue's templating language. This allows us to write our components in plain JavaScript with no compilation step.
Until now we have had no problems. However, my teammate recently made a component that "forwards" its default slot to a child component. The component hierarchy looks like
A
->B
->C
.A
passes content intoB
's default slot, and thenB
forwards its slot content toC
. So far so good.Our problem is that whenever
B
re-renders, the slot content is also re-rendered, leading to serious performance degradation. You can see the problem manifest in this Playground. Notice how the timestamp follows the cursor as you move the mouse around the preview. Now, the timestamp should not change. The render function passed in byA
(Date.now
), is not reactive and so only needs to be called once.Interestingly, when the same set of components is written using Vue templates, the correct behaviour emerges. You can see that in this Playground.
Selecting Follow.vue's "JS" tab reveals the compiled template code. This is the working render function, and it appears to be aware that the slot is being forwarded:
My question is, how do I get the behaviour of the working example using only render functions? Is it possible?
Beta Was this translation helpful? Give feedback.
All reactions