Ionic version:
[cli] 5.2.2
[@ionic/react] 0.0.6
Current behavior:
When trying to remove a slide to dynamically generated slides, the following uncaught exception occurs:
react-dom.development.js:9279 Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
Expected behavior:
The slides should be updated to reflect the correct number of slides, without causing any errors.
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, {useState} from 'react';
const slideOpts = {
initialSlide: 0,
speed: 400
};
const Home: React.FunctionComponent = () => {
const [slides, setSlides] = useState([
{id: '1', text: 'Slide 1'},
{id: '2', text: 'Slide 2'},
{id: '3', text: 'Slide 3'},
{id: '4', text: 'Slide 4'},
{id: '5', text: 'Slide 5'}
]);
return (
<>
<IonHeader>
<IonToolbar>
<IonTitle>Ionic Blank</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonButton onClick={() => setSlides(slides.slice(1))}>
Remove first slide
</IonButton>
<IonSlides pager={true} options={slideOpts}>
{slides.map(slide => (<IonSlide key={slide.id}><h1>{slide.text}</h1></IonSlide>))}
</IonSlides>
</IonContent>
</>
);
};
export default Home;
ionic serve
Related code:
https://github.com/m-spyratos/-ionic-react-slides-remove
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
I'm experiencing a the same issue. Triggered when navigation between two screens having dynamically generated slides.
I don't know if it helps, but in React, I had the same issue with Materialize Stepper and solved it wrapping the step-content component by <React.Fragment>
instead <div>
.
Although ionic is not react-based, the ideia is the <React.Fragment>
is kind a "neutral" component that does not inject invalid nodes in the DOM as div could do.
Maybe it is related.
@lfernando-silva Thanks for the suggestion, but this doesn't work for me unfortunately.
const slides = groups.map((group) => (
<IonSlide key={group.name}>
Slide
</IonSlide>
));
<IonSlides
pager={false}
options={slideOpts}
ref={_slidesRef}
onIonSlideDidChange={() => handleSlideChange()}
>
{slides}
</IonSlides>
If i wrap the slides in a div <div>{slides}</div>
it breaks the IonSlides but the error goes away. However just wrapping it in a <Fragment>
doesn't do anything.
I was meaning to change the IonSlide
component behavior to "inherit" a Fragment-like component.
I don't know if it is possible, but for example, in the React Material-UI Button Component, you can pass a component
param to change the behavior of the Button
, where the default is the HTML button
. Passing a
instead, the Button
component acts as a HTML <a>
tag.
The IonSlide
is not very well documented, but as far as I can tell from looking through the code this isn't possible.
Here is a workaround. Change the IonSlides
key
when your slides list changes. It's not a universal workaround but it's suitable for some cases:
// When the `item` slides ids don't change
<IonSlides key={item.id}>
{item.slides.map(slide => (<IonSlide key={slide.id}><h1>{slide.text}</h1></IonSlide>))}
</IonSlides>
// When the `item` slides ids change
<IonSlides key={item.slides.map(slide => slide.id).join('_')}>
{item.slides.map(slide => (<IonSlide key={slide.id}><h1>{slide.text}</h1></IonSlide>))}
</IonSlides>
It makes the ion-slides
be destroyed and created (instead of crashing) on every slides change.
@Finesse
Thanks for this, can't believe I didn't think of that. This works for my case and I guess for now this will be the solution 馃憤
Thanks for the issue! Is this still an issue using the official Ionic React version, the latest being 5.1.1
at the time of writing?
This is still an issue with 5.0.7.
Can confirm this issue
I just replaced my IonSlides by react-id-swiper and it works like a charm.
@brandyscarney I can also confirm this is still an issue on 5.1.1
Any chance this ticket can get an update? This issue is actually quite crippling to ionic display functionality.
Still experiencing this. Link to a CodeSandbox;
Search Miami, select the city, and wait for the weather to load. Then search Berlin, select it, and wait for the weather to load. The application will crash, and the error message pinpoints it to <IonSlide>
being the culprit. As the commenter above said, this is CRIPPLING to functionality, _especially_ something as basic as a dynamically-rendered carousel.
In case anyone needs it, I created a simple tik-tok state machine which seems to work well:
const Swiper: React.FC<SwiperArguments> = (
{
elements,
}) => {
const elementReducer = (state: { elements: any[], tick: boolean }, action: { type: "set_elems" | "tok", elements: any[] }) => {
switch (action.type) {
case "tok":
return {...state, tick: false};
case "set_elems":
return {...state, elements: action.elements, tick: true}
default:
return state;
}
}
const [data, elementsDispatch] = useReducer(elementReducer, {elements: [], tick: false});
useEffect(() => {
elementsDispatch({type: "set_elems", elements: elements});
}, [elements]);
useEffect(() => {
if (data.tick) {
elementsDispatch({type: "tok", elements: []});
}
}, [data.tick])
return (
<div className="slider-container">
{(data.tick && (
<div className="spinner-wrapper">
<IonSpinner/>
</div>
)) || (
<IonSlides>
{ data.elements.map(((value, index) => {
...
}))}
</IonSlides>
)}
</div>
);
}
It basically removes the swiper for one tick before rebuilding it completely every time the elements change.
Most helpful comment
Here is a workaround. Change the
IonSlides
key
when your slides list changes. It's not a universal workaround but it's suitable for some cases:It makes the
ion-slides
be destroyed and created (instead of crashing) on every slides change.