Reputation: 105
I'm trying to build something like this in React Native. It will stretch across the whole page and will loop infinitely, there will be a 'next' and 'previous' button.
I'm new to React Native (coming from React), so am a little unsure about how to implement it.
I found this guide on YouTube helpful to get something very basic up and running.
Here is the code I have so far:
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {withTheme} from 'react-native-paper';
import {
View,
StyleSheet,
Text,
Dimensions,
Image,
FlatList,
Pressable,
} from 'react-native';
import PrismicText from '../prismicText';
const {width: windowWidth, height: windowHeight} = Dimensions.get('window');
const Slide = ({data}) => {
return (
<View
style={{
height: 400,
width: 300,
justifyContent: 'center',
alignItems: 'center',
marginRight: 15,
}}>
<Image
source={{uri: data.image}}
style={{width: '100%', height: '100%', borderRadius: 16}}></Image>
</View>
);
};
const Carousel = ({slice, theme}) => {
const slideList = slice.items.map((item, index) => {
return {
id: index,
image: item.image.url,
};
});
const {colors, isTabletOrMobileDevice} = theme;
const styles = isTabletOrMobileDevice ? mobileStyles : desktopStyles;
const flatListRef = useRef(null);
const viewConfig = {viewAreaCoveragePercentThreshold: 50};
const [activeIndex, setActiveIndex] = useState(4);
const onViewRef = useRef(({changed}) => {
if (changed[0].isViewable) {
setActiveIndex(changed[0].index);
}
});
const handlePressLeft = () => {
if (activeIndex === 0)
return flatListRef.current?.scrollToIndex({
animated: true,
index: slideList.length - 1,
});
flatListRef.current?.scrollToIndex({
index: activeIndex - 1,
});
};
const handlePressRight = () => {
if (activeIndex === slideList.length - 1)
return flatListRef.current?.scrollToIndex({
animated: true,
index: 0,
});
flatListRef.current?.scrollToIndex({
index: activeIndex + 1,
});
};
return (
<>
<View
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 8,
}}>
<Pressable style={[styles.chevron]} onPress={handlePressLeft}>
Left
</Pressable>
<Pressable style={[styles.chevron]} onPress={handlePressRight}>
Right
</Pressable>
</View>
<FlatList
ref={ref => (flatListRef.current = ref)}
data={slideList}
horizontal
showsHorizontalScrollIndicator={false}
snapToAlignment="center"
pagingEnabled
viewabilityConfig={viewConfig}
onViewableItemsChanged={onViewRef.current}
renderItem={({item}, i) => <Slide data={item} />}
keyExtractor={item => item}
/>
<View style={styles.index}>
<Text category={'c2'} style={styles.indexText}>
{activeIndex + 1} of {slideList.length} photos
</Text>
</View>
</>
);
};
const mobileStyles = StyleSheet.create({});
const desktopStyles = StyleSheet.create({});
export default withTheme(Carousel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
The problems I'm experiencing with this code:
My feeling is that there are two issues to be addressed:
I've spent a lot of time looking at this and can't seem to figure it out. Any help would be much appreciated.
Upvotes: 1
Views: 1849
Reputation: 1053
You should try react-native-reanimated-carousel.
Why?
Upvotes: 0
Reputation: 9856
The first issue is easy to fix. You are expecting that the FlatList
scrolls initially to the initial activeIndex
, but you are not telling the FlatList
to do so. There is a prop called initialScrollIndex
that is designed for this purpose.
<FlatList
initialScrollIndex={4}
...
The second issue is caused by a faulty implementation of the functions handlePressLeft
and handlePressRight
as well as providing
const onViewRef = useRef(({changed}) => {
if (changed[0].isViewable) {
setActiveIndex(changed[0].index);
}
});
I have removed the above completely.
I have changed the activeIndex
state to the following.
const [activeIndex, setActiveIndex] = useState({index: 4, direction: 'right'});
I have changed the handlePressLeft
and handlePressRight
functions to the following.
const handlePressLeft = () => {
setActiveIndex((prev) => ({index: prev.index - 1, direction: 'left'}));
};
const handlePressRight = () => {
setActiveIndex((prev) => ({index: prev.index + 1, direction: 'right'}));
};
I have created an effect as follows.
React.useEffect(() => {
if (activeIndex.index === slideList.length - 1 && activeIndex.direction === 'right') {
setActiveIndex({index: 0, direction: 'right'});
} else if (activeIndex.index < 0 && activeIndex.direction === 'left') {
setActiveIndex({index: slideList.length - 2, direction: 'left'})
} else {
flatListRef.current?.scrollToIndex({
animated: true,
index: activeIndex.index,
});
}
}, [activeIndex, slideList.length]);
I have implemented an adapted snack without images and using a dummy array.
Upvotes: 1
Reputation: 2366
You can use the below third-party library to achieve the above one quickly.
You can check all the examples and use them according to your requirement.
Hope it will help you!
Upvotes: 0