Ionic version:
[cli] 5.2.2
[@ionic/react] 0.0.6
Current behavior:
When slides are being added dynamically, the layout of the slides component breaks and newly added slides sit outside of the swiper-wrapper. Issue persists even after calling component's update() method.
Expected behavior:
When adding additional slides, slides component should be updated to properly display those added slides.
Steps to reproduce:
Start a new React blank project:
ionic start slides blank --type=react
Use the following Home.tsx template:
import {IonContent, IonHeader, IonTitle, IonToolbar, IonSlides, IonSlide, IonButton} from '@ionic/react';
import React from 'react';
const slideOpts = {
initialSlide: 0,
speed: 400
};
class Home extends React.Component<any, any> {
private _slidesRef = React.createRef<any>();
constructor(props: any) {
super(props);
this.state = {
slides: [
'1 Slide',
'2 Slide',
]
};
}
onSlidesAdd() {
this.setState({
slides: [
'1 Slide',
'2 Slide',
'3 Slide',
'4 Slide',
'5 Slide'
]
}, async () => {
await this._slidesRef.current.update();
console.log('IonSlides updated, but issue persists');
});
}
render() {
const {slides} = this.state;
return (
<>
<IonHeader>
<IonToolbar>
<IonTitle>Ionic Blank</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonButton onClick={() => this.onSlidesAdd()}>
Add slides
</IonButton>
<IonSlides ref={this._slidesRef} pager={true} options={slideOpts}>
{slides.map((slide: any) => (
<IonSlide key={parseInt(slide)}>
<h1>{slide}</h1>
</IonSlide>
))}
</IonSlides>
</IonContent>
</>
);
}
}
export default Home;
ionic serveRelated code:
https://github.com/m-spyratos/-ionic-react-ion-slides-adding
Other information:
Ionic info:
Ionic:
Ionic CLI: 5.2.2 (/usr/local/lib/node_modules/ionic)
Ionic Framework: @ionic/react 0.0.6
Utility:
cordova-res: not installed
native-run: not installed
System:
NodeJS: v10.16.0 (/usr/local/bin/node)
npm: 6.10.0
OS: macOS Mojave
@m-spyratos I have the same problem. Did you manage to make the "options" property work?
Same for here, I have observed the same DOM insertion issue in other Stencil.js based components. Is there any fix right now ?
Also running into this. Can anyone shed some light on a possible fix?
+1
I faced this probelm also but I get to fix it.
I think the IonSlides did not re-initialize when prop [slides] updated from empty array.
The possible solution will be
{ slides.length > 0 &&
<IonSlides ref={this._slidesRef} pager={true} options={slideOpts}>
{slides.map((slide: any) => (
<IonSlide key={parseInt(slide)}>
<h1>{slide}</h1>
</IonSlide>
))}
</IonSlides>
}
@lai5203 that seems to only check that the array is not empty. The problem persists if the array is not empty initially and then more slides are added dynamically to the array.
This issue also exists when using the web component version
Is there a quick fix / workaround to this issue ?
Any fix about this question too? I've tried to use update method passing the Ref with useRef(), but doesn't work =(
+1
The only way I could make it work when new slides are added dynamically was using directly the Swiper API.
initialSlides should never change. After the first render, any new images will be added using appendSlide
this is more or less what I did:
const slidesRef = useRef<HTMLIonSlidesElement>(null);
useEffect(() => {
const handler = async (): Promise<void> => {
const swiper = await slidesRef.current.getSwiper();
swiper.appendSlide(
`<ion-slide class='swiper-slide'><img class="landscape" alt decoding="async" src="${newUrl}"/></ion-slide>`
);
swiper.update();
};
handler();
}, [newUrl]);
return (
<IonContent fullscreen scrollY={false}>
{initialSlides.length && (
<Slides ref={slidesRef}>
{initialSlides.map(slide => (
<IonSlide>blablabla</IonSlide>
))}
</Slides>
)}
</IonContent>
);
Same method that @javipascual , but i use the ReactDOMServer.renderToString() method in order to call my React Component into the appendSlide method.
Like this :
const slidesRef: any = document.querySelector(`#${id}`);
const next = async () => {
const swiper: any = await slidesRef.getSwiper();
swiper.appendSlide(generateSlides(limit));
swiper.update();
setLimit(limit + step);
}
const generateSlides = (offset = 0) => {
let toReturn: any = [];
txs.slice(offset , offset + step).map((tx: any, key: any) => {
toReturn.push(
ReactDOMServer.renderToString(
<IonSlide key={key}>
<TxCard tx={tx}></TxCard>
</IonSlide>
)
)
})
return toReturn;
}
return (
<div className="txs-slider">
<div className="slider-context">
<h2>{title}</h2>
<p>{description}</p>
</div>
<IonSlides id={id} mode='md' scrollbar options={slideOpts} onIonSlideReachEnd={limit < 50 ? next : undefined}>
{
txs.slice(0, step).map((tx, key) => {
return (
<IonSlide key={key}>
<TxCard tx={tx}></TxCard>
</IonSlide>
);
})
}
</IonSlides>
</div>
);
Thanks to @Guillaume-Souillard, finally I solved it with ur workaround!
But are there any news on this? It's so sobering this.slidesRef.current.update(); isn't properly working.
one hack, that i dont like, that worked for me was setting a key value to
<IonSlides key={lengthOfArray + 'someUniqueString'}>
so anytime a new slidewas added, the slides would be properly formatted. Unfortunately we have to result to these kinds of measures whenever the library doesnt work. but since this isn't working properly as intended, im just going to change my UI to not use slides and hopefully this will become fixed soon.
@mrowe009 still way better than calling the Swiper API directly, thanks:)
Because in my case, I somehow made it to create a vertical and horizontal scrollable slides object.
@csaar95 okay, just keep in mind, the reason i said i didn't like that hack is because whenever you change the key in react, it re-renders that component and its children. so if you have a few items, its ok, but if you have many, or child components that are render heavy, then you'll see very laggy UI performance.
I have tried the solution of @mrowe009. At each slide change, I add one slide at the end and it re-renders the slider. It is causing a white screen for a few miliseconds before the slider re-renders. This is disturbing for the user. Do you have this as well?
Same method that @javipascual , but i use the
ReactDOMServer.renderToString()method in order to call my React Component into the appendSlide method.Like this :
const slidesRef: any = document.querySelector(`#${id}`); const next = async () => { const swiper: any = await slidesRef.getSwiper(); swiper.appendSlide(generateSlides(limit)); swiper.update(); setLimit(limit + step); } const generateSlides = (offset = 0) => { let toReturn: any = []; txs.slice(offset , offset + step).map((tx: any, key: any) => { toReturn.push( ReactDOMServer.renderToString( <IonSlide key={key}> <TxCard tx={tx}></TxCard> </IonSlide> ) ) }) return toReturn; } return ( <div className="txs-slider"> <div className="slider-context"> <h2>{title}</h2> <p>{description}</p> </div> <IonSlides id={id} mode='md' scrollbar options={slideOpts} onIonSlideReachEnd={limit < 50 ? next : undefined}> { txs.slice(0, step).map((tx, key) => { return ( <IonSlide key={key}> <TxCard tx={tx}></TxCard> </IonSlide> ); }) } </IonSlides> </div> );
@Guillaume-Souillard I have tried your solution but it seems the javascript and the Ionic attributes (like color="primary" in a IonText) are just ignored by ReactDOMServer.renderToString(). Do you have the same issue and what could be the workaround? Thanks
After some tests, the issue is not that the update method is not called or not working.
The update function is actually called when new child are added.
However looking at the dom, we see that slides added dynamically are not added inside the <div class="swiper-wrapper">
@Toto5931 sorry for the very late response, so not sure if this is still a problem, but as i mentioned, that was a very hacky solution, and you might have ran into the reason why. Whenever you change the key of a component in React, it will re-render the entire component tree (the component with the key, and everything underneath it) so your white screen is probably being caused by all your components getting re-rendered and if its a lot of complex components with logic/async/effects/etc. it will cause laggy performance (white screens) until re-rendering is done
After some tests, the issue is not that the update method is not called or not working.
The update function is actually called when new child are added.
However looking at the dom, we see that slides added dynamically are not added inside the<div class="swiper-wrapper">
Hello @ggros have you found any solution to this....I am fetching my data dynamically from flask backend and the data is objects containing more object.
@javipascual @Guillaume-Souillard Your help would be also really helpful.
@naishal I can show u my workaround. Did it with the help of @mrowe009 (https://github.com/ionic-team/ionic-framework/issues/18784#issuecomment-625689657). U'll find the important part in line 30.
But keep in mind, my application rarely updates the amount of slides.
import { IonSlides } from '@ionic/react';
import React from 'react';
import './Slides.css';
import Slide from '../Slide/Slide'
import SlideObject from '../../models/SlideObject';
import SlideOptsObject from '../../models/SlideOptsObject';
interface SlidesProps {
slides: SlideObject[],
slideOpts: SlideOptsObject,
setActiveDate: Function
};
interface SlidesState {
};
class Slides extends React.Component<SlidesProps, SlidesState> {
private slidesRef = React.createRef<any>();
componentDidUpdate(prevProps: SlidesProps) {
// IonSlides aktualisieren
if (this.props.slides.length !== prevProps.slides.length) {
this.slidesRef.current.update();
}
}
render() {
return (
// key ggf. bei Bugfix von Ionic entfernen - https://github.com/ionic-team/ionic/issues/18784
<IonSlides ref={this.slidesRef} scrollbar={true} options={this.props.slideOpts} key={this.props.slides.length}
onIonSlideDidChange={async() => this.props.setActiveDate(this.props.slides[await this.slidesRef.current.getActiveIndex()].date)}
>
{this.props.slides.map((slide, i) => {
return <Slide slide={slide} key={i} />;
})}
</IonSlides>
);
}
};
export default Slides;
After some tests, the issue is not that the update method is not called or not working.
The update function is actually called when new child are added.
However looking at the dom, we see that slides added dynamically are not added inside the<div class="swiper-wrapper">Hello @ggros have you found any solution to this....I am fetching my data dynamically from flask backend and the data is objects containing more object.
@javipascual @Guillaume-Souillard Your help would be also really helpful.
@naishal in the end, I used swiper directly without IonSlides. I also created a PR with a suggested fix and a test that will demonstrate the issue. https://github.com/ionic-team/ionic-framework/pull/21655
After some tests, the issue is not that the update method is not called or not working.
The update function is actually called when new child are added.
However looking at the dom, we see that slides added dynamically are not added inside the<div class="swiper-wrapper">Hello @ggros have you found any solution to this....I am fetching my data dynamically from flask backend and the data is objects containing more object.
@javipascual @Guillaume-Souillard Your help would be also really helpful.@naishal in the end, I used swiper directly without IonSlides. I also created a PR with a suggested fix and a test that will demonstrate the issue. #21655
@ggros can u post that part of your code, it would be really helpful for me to understand the flow.
@naishal I recommend to use the official react wrapper and check the corresponding samples https://swiperjs.com/react/
@ggros when I use the official react wrapper with Ionic, the slide is stuck at the middle as soon as the mouse button is up. The "spring effect" does not work. I left the issue above. Do you have the same problem?
Hi, @liamdebeasi do you know when this bug will be solved? I am stuck in my project because of it. Thanks a lot
@liamdebeasi according to you, when do you think you will have time to solve this issue? I would really need it. Thanks!
@ggros when I use the official react wrapper with Ionic, the slide is stuck at the middle as soon as the mouse button is up. The "spring effect" does not work. I left the issue above. Do you have the same problem?
@Toto5931 no, I do not have that problem, the slides are working for me. If you want to use ion slide, yyou can also check my pull request.
it seems the slider react component as proposed by @ggros breaks as soon as put inside a ionic component
such as grid or even loaded by ionic-router-outlet.
Most helpful comment
I faced this probelm also but I get to fix it.
I think the IonSlides did not re-initialize when prop [slides] updated from empty array.
The possible solution will be