I am making a custom image carousel or an image slider in react native without using any packages, the issue is that the dot indicators on the bottom are changing fine after 3 second intervals but the image doesn't slides automatically. There is some minor issue which I am unable to find. If you can help in getting this fixed without changing much of the code it would be really nice. thanks in advance.
Here is the full component code.
import React, {useEffect, useState, useRef} from 'react';
import {StyleSheet, ScrollView, View, Dimensions, Text} from 'react-native';
import {ActivityIndicator} from 'react-native';
import {Image} from 'react-native-elements';
const HomeCarousel = () => {
const [dimension, setDimension] = useState(Dimensions.get('window'));
const [selectedIndex, setSelectedIndex] = useState(0);
const scrollRef = useRef();
const onChange = () => {
setDimension(Dimensions.get('window'));
};
useEffect(() => {
Dimensions.addEventListener('change', onChange);
return () => {
Dimensions.removeEventListener('change', onChange);
};
});
useEffect(() => {
setInterval(() => {
setSelectedIndex(prevSelectedIndex =>
prevSelectedIndex === carouselImages.length - 1
? 0
: prevSelectedIndex + 1,
);
() => {
scrollRef.current.scrollTo({
animated: true,
y: 0,
x: dimension.width * selectedIndex,
});
};
}, 3000);
}, []);
const carouselImages = [
{url: 'https://i.ibb.co/FDwNR9d/img1.jpg'},
{url: 'https://i.ibb.co/7G5qqGY/1.jpg'},
{url: 'https://i.ibb.co/Jx7xqf4/pexels-august-de-richelieu-4427816.jpg'},
{url: 'https://i.ibb.co/GV08J9f/pexels-pixabay-267202.jpg'},
{url: 'https://i.ibb.co/sK92ZhC/pexels-karolina-grabowska-4210860.jpg'},
];
const setIndex = event => {
let viewSize = event.nativeEvent.layoutMeasurement.width;
let contentOffset = event.nativeEvent.contentOffset.x;
let carouselIndex = Math.floor(contentOffset / viewSize);
setSelectedIndex(carouselIndex);
};
return (
<View style={{width: dimension.width}}>
<ScrollView
horizontal
ref={scrollRef}
onMomentumScrollEnd={setIndex}
showsHorizontalScrollIndicator={false}
pagingEnabled>
{carouselImages.map((value, key) => (
<Image
source={{uri: `${value.url}`}}
style={{width: dimension?.width, height: 250, resizeMode: 'cover'}}
PlaceholderContent={<ActivityIndicator />}
/>
))}
</ScrollView>
<View
style={{
flexDirection: 'row',
position: 'absolute',
bottom: 0,
alignSelf: 'center',
}}>
{carouselImages.map((val, key) => (
<Text
key={key}
style={key === selectedIndex ? {color: 'white'} : {color: '#888'}}>
⬤
</Text>
))}
</View>
</View>
);
};
const styles = StyleSheet.create({});
export default HomeCarousel;
Your images aren't being update because of 2 things:
// You're just defining the function here and not actually calling it
() => {
scrollRef.current.scrollTo({
animated: true,
y: 0,
x: dimension.width * selectedIndex,
});
};
useEffect
doesn't have any dependencies, which means its callback won't be changed, and therefore the selectedIndex
inside of it will always be 0:useEffect(() => {
setInterval(() => {
setSelectedIndex(prevSelectedIndex =>
prevSelectedIndex === carouselImages.length - 1
? 0
: prevSelectedIndex + 1,
);
() => {
scrollRef.current.scrollTo({
animated: true,
y: 0,
// Since there are no dependencies, this callback won't ever be changed and selectedIndex will always be 0
x: dimension.width * selectedIndex,
});
};
}, 3000);
// You have no dependencies here
}, []);
My suggestions for improvements/fixes would be:
useEffect
callback and apply a useCallback
hook with selectedIndex
dependency. You should also calculate the value of the new index outside of the setSelectedIndex
hook, because setState/useState
doesn't update immediately :// You should also use an intervalId so that you can clear the interval/reset when needed
let intervalId = null;
const HomeCarousel = () => {
// ... rest of the HomeCarousel component
const onSlideChange = useCallback(() => {
// Calculate newIndex here and use it to update your state and to scroll to the new slide
const newIndex = selectedIndex === carouselImages.length - 1 ? 0 : selectedIndex + 1;
setSelectedIndex(newIndex);
scrollRef?.current?.scrollTo({
animated: true,
y: 0,
x: dimension.width * newIndex,
});
}, [selectedIndex]);
const startInterval = useCallback(() => {
intervalId = setInterval(onSlideChange, 3000);
}, [onSlideChange]);
useEffect(() => {
startInterval();
return () => {
// Clear the interval when component unmounts, otherwise you could have memory leaks
clearInterval(intervalId);
};
}, [onSlideChange]);
// ... rest of the HomeCarousel component
};
const onTouchStart = () => {
// As soon as the user touches the slide, stop the automatic sliding
clearInterval(intervalId);
};
const onTouchEnd = () => {
// As soon as the user stops touching the slide, releases it, start the automatic sliding again
startInterval();
};
return (
<ScrollView
// ... Other scrollview props
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}>
{/* ... Rest of the component */}
const carouselImages = [ carousel1, carousel2, carousel3, carousel4, carousel5 ]; const Carousel = () => { const [current, setCurrent] = useState(0); useEffect(() => { const next = (current + 1) % carouselImages.length; const id = setTimeout(() => setCurrent(next), 3000); return () => clearTimeout(id); }, [current]); return ( <div className="banner"> <div className="slider"> <img src={carouselImages[current]} alt="carousel" /> </div> <div className="overlay"></div> </div> );
Here is the full working code.
import React, {useEffect, useState, useRef, useCallback} from 'react';
import {StyleSheet, ScrollView, View, Dimensions, Text} from 'react-native';
import {ActivityIndicator} from 'react-native';
import {Image} from 'react-native-elements';
const HomeCarousel = () => {
const [dimension, setDimension] = useState(Dimensions.get('window'));
const [selectedIndex, setSelectedIndex] = useState(0);
const scrollRef = useRef();
let intervalId = null;
const onChange = () => {
setDimension(Dimensions.get('window'));
};
useEffect(() => {
Dimensions.addEventListener('change', onChange);
return () => {
Dimensions.removeEventListener('change', onChange);
};
});
const onSlideChange = useCallback(() => {
// Calculate newIndex here and use it to update your state and to scroll to the new slide
const newIndex =
selectedIndex === carouselImages.length - 1 ? 0 : selectedIndex + 1;
setSelectedIndex(newIndex);
scrollRef?.current?.scrollTo({
animated: true,
y: 0,
x: dimension.width * newIndex,
});
}, [selectedIndex]);
const startInterval = useCallback(() => {
intervalId = setInterval(onSlideChange, 3000);
}, [onSlideChange]);
useEffect(() => {
startInterval();
return () => {
// Clear the interval when component unmounts, otherwise you could have memory leaks
clearInterval(intervalId);
};
}, [onSlideChange]);
const onTouchStart = () => {
// As soon as the user touches the slide, stop the automatic sliding
clearInterval(intervalId);
};
const onTouchEnd = () => {
// As soon as the user stops touching the slide, releases it, start the automatic sliding again
startInterval();
};
const carouselImages = [
{url: 'https://i.ibb.co/FDwNR9d/img1.jpg'},
{url: 'https://i.ibb.co/7G5qqGY/1.jpg'},
{url: 'https://i.ibb.co/Jx7xqf4/pexels-august-de-richelieu-4427816.jpg'},
{url: 'https://i.ibb.co/GV08J9f/pexels-pixabay-267202.jpg'},
{url: 'https://i.ibb.co/sK92ZhC/pexels-karolina-grabowska-4210860.jpg'},
];
const setIndex = event => {
let viewSize = event.nativeEvent.layoutMeasurement.width;
let contentOffset = event.nativeEvent.contentOffset.x;
let carouselIndex = Math.floor(contentOffset / viewSize);
setSelectedIndex(carouselIndex);
};
return (
<View style={{width: dimension.width}}>
<ScrollView
horizontal
ref={scrollRef}
onMomentumScrollEnd={setIndex}
showsHorizontalScrollIndicator={false}
onTouchStart={onTouchStart}
onTouchEnd={onTouchEnd}
pagingEnabled>
{carouselImages.map((value, key) => (
<Image
source={{uri: `${value.url}`}}
style={{width: dimension?.width, height: 250, resizeMode: 'cover'}}
PlaceholderContent={<ActivityIndicator />}
/>
))}
</ScrollView>
<View
style={{
flexDirection: 'row',
position: 'absolute',
bottom: 0,
alignSelf: 'center',
}}>
{carouselImages.map((val, key) => (
<Text
key={key}
style={key === selectedIndex ? {color: 'white'} : {color: '#888'}}>
⬤
</Text>
))}
</View>
</View>
);
};
const styles = StyleSheet.create({});
export default HomeCarousel;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.