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:
- I am setting the initial state of the active index to 4, but active index always starts at 0
- Clicking the 'right' button doesn't seem to change the active index
- Clicking the 'right' button will move the carousel along by 1 increment, but it won't go any further (even on smaller viewports like mobile where you can only see 1.5 cards, so it should move along many times to be able to see all of the cards)
- Clicking the 'left' button seems to have the same issues as above
- There is no infinite loop of the slides
My feeling is that there are two issues to be addressed:
- Active index is broken and needs to be fixed
- Modifications required to make the number of cards on the viewport responsive
I've spent a lot of time looking at this and can't seem to figure it out. Any help would be much appreciated.
CodePudding user response:
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.
CodePudding user response:
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!
CodePudding user response:
You should try react-native-reanimated-carousel.
Why?
- highly customizable easy and fast to implement any carousel
- It's new and it uses react-native-reanimated for better performance (by running animations on the UI thread, rather than on JS thread)
- solves all the issues that react-native-snap-carousel has (which is deprecated and has lots of bugs)
- solves all the issues that you have and handles many edge cases that you may have forgotten about (in case you want to implement it by yourself)