Vue: default slot is invoked outside the render function in JSX, logging a warning on every Swiper
Check that this is really a bug
- I confirm
Reproduction link
https://codesandbox.io/s/github/jarofbilly/swiper-vue-jsx-slot-warning
Source: https://github.com/jarofbilly/swiper-vue-jsx-slot-warning Also runs on StackBlitz: https://stackblitz.com/github/jarofbilly/swiper-vue-jsx-slot-warning
Bug description
Every <Swiper> in a Vue JSX/TSX project logs this on mount:
[Vue warn]: Slot "default" invoked outside of the render function: this will not track
dependencies used in the slot. Invoke the slot function inside the render function instead.This was reported in #4974 (Swiper 7.0.6) and closed with "it is actually fine as everything works correctly, just ignore this warning". That assessment still holds and I am not disputing it. I tested reactive state read only inside the slot and it updates correctly with or without a workaround, so nothing is broken. I am opening a fresh issue rather than commenting on a closed one because the ask is different: the warning is still present four years later in 14.2.0, it is unavoidable noise for every Vue JSX consumer, and I am offering to fix it.
getChildren() in swiper-vue.mjs calls slots.default(). It runs from three places:
| Line (14.2.0) | Context | Warns |
|---|---|---|
| 393 | directly in setup() |
yes |
| 397 | onBeforeBreakpoint |
yes |
| 489 | inside the returned render function | no |
Line 489 is fine. The other two run outside a render pass, so currentRenderingInstance is
null and Vue warns.
Why this only affects JSX
The two compilers wrap slots differently. Same component, same slot:
// @vue/compiler-sfc
default: _withCtx(() => [ ... ])
// @vue/babel-plugin-jsx (both array-children and object-slot forms)
default: () => [ ... ]_withCtx sets the rendering instance for the duration of the call, so SFC users never see the
warning even though the same setup-time call happens for them too. That is why this looks rare
despite affecting every JSX consumer.
Possible fix
slidesRef is consumed at lines 415, 418 and 434, all of which run at or after mount. Vue's
order is setup() then render() then mounted(), and the render function already calls
getChildren() at line 489, so slidesRef is populated before anything reads it. On that
reading the setup() call looks redundant. onBeforeBreakpoint sets breakpointChanged.value,
which triggers a re-render and therefore another getChildren(), so it may be redundant too.
Two things would need verifying before that could ship, which is what a PR and your test suite would settle:
- whether
oldSlidesRefstarting empty rather than pre-seeded changes first-update behaviour throughgetChangedParams - whether breakpoint changes still pick up slide changes in the right order
Happy to be told a different approach is preferred.
Expected Behavior
No warning, since nothing is actually wrong.
Actual Behavior
Every <Swiper> instance logs the warning on mount. In an app with several carousels the console
fills with it on every page load.
Swiper version
14.2.0. Also reproduces on 14.1.0 (same code at lines 393/397/489) and originally reported in #4974 against 7.0.6.
Platform/Target and Browser Versions
Windows 11, Firefox 154. Compiler level rather than browser specific.
Reproduction environment: vue 3.5.42, @vue/babel-plugin-jsx 1.5.0 via @vitejs/plugin-vue-jsx
4.2.0, vite 6.4.3. Originally hit on @vue/babel-plugin-jsx 2.0.1 via @vitejs/plugin-vue-jsx
5.1.x, so it is not specific to one major of the JSX plugin.
Validations
- Follow our Code of Conduct
- Read the docs.
- Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
- Make sure this is a Swiper issue and not a framework-specific issue
Would you like to open a PR for this bug?
- I'm willing to open a PR
Source: nolimits4web/swiper