Set Width of a View Programmatically React Native - react-native

I have a horizontal list of Views in react native. Views have different widths.
How can I set the width of each element equal to the widest one?
I have tried:
const textViewRef = useRef<any>([]);
textViewRef.current = btnData.map((_, i) => textViewRef.current[i] ?? createRef());
const ref = useRef<TouchableOpacity | null>(null);
useEffect(() => {
let maxWidth = 0;
for (const el of textViewRef.current) {
el.current?.measure((x: number, y: number, width: number, height: number, pageX: number, pageY: number) => {
if (width > maxWidth) {
maxWidth = width;
if (el.current) {
if (el.current.style) {
el.current.style.minWidth = maxWidth;
}
}
}
});
}
}, [textViewRef.current]);
<FlatList
showsHorizontalScrollIndicator={false}
horizontal
data={btnData}
contentContainerStyle={{ flexGrow: 0 }}
renderItem={({ item, index }) => (
<TouchableOpacity ref={textViewRef.current[index] as any} key={index} style={[styles.navBtn]}>
<Body2>{item.title}</Body2>
</TouchableOpacity>
)}
/>

Related

How to get the active index of an item of a flatlist?

I'm trying to highlight an indicator based on the active item of a flatlist , How i'm trying to do it is like this :
const [activeIndex, setActiveIndex] = useState(0);
const windowWidth = useWindowDimensions().width;
const onFlatlistUpdate = useCallback(({ viewableItems }: any) => {
if (viewableItems.length > 0) {
setActiveIndex(viewableItems[0].index);
}
console.log(viewableItems);
}, []);
const renderItem: ListRenderItem<string> = ({ item }) => {
return (
<Image
style={[styles.image, { width: windowWidth - 40 }]}
source={{ uri: item }}
/>
);
};
return (
<View style={styles.root}>
<FlatList
keyExtractor={(key) => key}
decelerationRate="fast"
snapToInterval={windowWidth - 20}
snapToAlignment="center"
data={images}
renderItem={renderItem}
horizontal
showsHorizontalScrollIndicator={false}
viewabilityConfig={{
viewAreaCoveragePercentThreshold: 50,
}}
onViewableItemsChanged={onFlatlistUpdate}
/>
I'm using a callback so don't get the changing onviewableitems on the fly isn't supported but it seems like the
<View style={styles.dots}>
{images.map((image, index) => (
<View
style={[
styles.dot,
{
backgroundColor: index === activeIndex ? "#c9c9c9" : " #ededed",
},
]}
/>
))}
</View>
when i change the item on the flatlist the chosen item becomes highlighted and all previous highlighted items are also highlighted if that makes sense , so if i'm on the first image of the flatlist the first dots is highlighted
Render item provides an index prop to your component
const renderItem: ListRenderItem<string> = ({ item, index }) => {

create Carousel in React Native using FlatList

I'm creating a carousel component in React Native using a FlatList and I use useState hook to control the index of image, images load properly and the problem is I cant use my buttons to control the carousel. for example when I tap on right arrow first time doesn't work but when I tap again it goes to next image.
here's my code:
const { width: windowWidth, height: windowHeight } = Dimensions.get("window");
const slideList = Array.from({ length: 5 }).map((_, i) => {
return {
id: i.toString(),
image: `https://picsum.photos/1440/2842?random=${i}`,
};
});
const Carousel = () => {
const [current, setCurrent] = useState(0);
const length = slideList.length;
const flatListRef = useRef();
const renderItem = ({ item }) => {
const arr = Object.values( item );
return (
<View style={styles.imagesContainer}>
<Image style={styles.image} source={{ uri: item.image }} />
</View>
);
}
const goNextSlide = () => {
setCurrent(current < length -1 ? current + 1 : 0);
flatListRef.current.scrollToIndex({ index: current, animated: true });
};
const goPrevSlide = () => {
setCurrent(current <= length - 1 && current >= 0 ? current -1 : 0);
flatListRef.current.scrollToIndex({ index: current, animated: true });
};
console.log(current)
return (
<View style={styles.screen}>
<View style={styles.controls}>
<TouchableOpacity style={styles.controlleft} onPress={goPrevSlide}>
<CarouselLeftArrow style={styles.leftArrow} size={28} fill='black' />
</TouchableOpacity>
<TouchableOpacity style={styles.controlRight} onPress={goNextSlide}>
<CarouselRightArrow style={styles.rightArrow} size={28} fill='black' />
</TouchableOpacity>
</View>
<FlatList
data={slideList}
keyExtractor={item => item.id}
renderItem={renderItem}
horizontal={true}
showsHorizontalScrollIndicator={false}
pagingEnabled={true}
ref={flatListRef}
/>
</View>
)
}
const styles = StyleSheet.create({
imagesContainer: {
width: windowWidth,
height: 250
},
image: {
width: '100%',
height: '100%'
},
controls: {
backgroundColor: 'yellow',
flexDirection: 'row',
justifyContent: 'space-between',
position: 'absolute',
zIndex: 2,
width: '100%',
top: 100
},
controlLeft: {
},
controlRight: {
}
})
export default Carousel;
any help would be appreciated.
goPrevSlide
setCurrent(current <= length - 1 && current >= 0 ? current -1 : 0);
When current >= 0 is not correct because if current equals zero then you set -1 to current in this case. Replace statement like setCurrent(current ? current - 1 : length - 1);
Since updating state is an async action, you can not handle updated variable immediately, you need to use effect hook in order to catch it.
useEffect(() => {
// fires every time when "current" is updated
flatListRef.current.scrollToIndex({ index: current, animated: true });
}, [current]);
Remove setCurrent function from both handler
try to give width and height to the images, you need that if source is uri.
see you code working at snack (without buttons)

Flatlist reference error when leaving the screen in a Carousel view

I have an autoscrolling carousel in React Native and everything is working fine with images scrolling through both automatically every X seconds and manually.
The problem is when I move away from that screen that I get the following error:
Here's is my full code...
const { width, height } = Dimensions.get("window");
let flatList;
function infiniteScroll(dataList) {
const numberOfData = dataList.length;
let scrollValue = 0,
scrolled = 0;
setInterval(function () {
scrolled++;
if (scrolled < numberOfData) scrollValue = scrollValue + width;
else {
scrollValue = 0;
scrolled = 0;
}
this.flatList.scrollToOffset({ animated: true, offset: scrollValue });
}, 3000);
}
const Carousel = (props) => {
const topTenVideos = useSelector(getTopTenVideos);
const dispatch = useDispatch();
const scrollX = new Animated.Value(0);
let position = Animated.divide(scrollX, width);
const [dataList, setDataList] = useState(topTenVideos);
const isFocused = useIsFocused();
useEffect(() => {
if (isFocused) {
setDataList(topTenVideos);
infiniteScroll(dataList);
}
}, [isFocused]);
const renderRow = (itemData) => {
return (
<CarouselItem
id={itemData.item.id}
img={itemData.item.poster}
title={itemData.item.title}
/>
);
};
return (
<View style={styles.screen}>
<FlatList
ref={(flatList) => {
this.flatList = flatList;
}}
horizontal
data={dataList}
pagingEnabled
scrollEnabled
snapToAlignment="center"
scrollEventThrottle={16}
decelerationRate={"fast"}
showsHorizontalScrollIndicator={false}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { x: scrollX } } },
])}
keyExtractor={(item, index) => "key" + index}
renderItem={renderRow}
/>
<View style={styles.dotView}>
{dataList.map((_, i) => {
let opacity = position.interpolate({
inputRange: [i - 1, i, i + 1],
outputRange: [0.3, 1, 0.3],
extrapolate: "clamp",
});
return (
<Animated.View
key={i}
style={{
opacity,
height: 8,
width: 8,
borderRadius: 6,
backgroundColor: "white",
margin: 8,
}}
/>
);
})}
</View>
</View>
);
};
It's complaining about this line this.flatList.scrollToOffset({ animated: true, offset: scrollValue });
}, 3000); which is inside my infiniteScroll function.
It looks like that when the screen loses focus, it is still searching for this.flatList.scrollToOffset.
You are not creating your ref properly, You have to use useRef hook if you are using functional component or createRef in the case of class component for making refs in your component
Have a look at this.
https://reactjs.org/docs/hooks-reference.html#useref
Create your ref like below.
const flatListRef = useRef(null)
<FlatList
ref={flatListRef}
// other props
/>
flatListRef.current.scrollToOffset({ animated: true, offset: scrollValue }) // access like this.

React Native Vertical Slider

I am Tring to make a screen in react native in which height of upper view is resizable via dragging from the bottom.enter image description here
Size of (1) increases on dragging (2)
So far I tried two approaches
1. PanResponder
const Chat = (props) => {
const orders = [
//Some Array
]
const Height = Dimensions.get('window').height
const boxHeight = useRef(new Animated.Value(Height / 3)).current
const panResponder = useRef(PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderMove: (evt, gestureState) => {
const nextValue = boxHeight+gestureState.dy
let newVal = nextValue >= maxHeight?maxHeight:nextValue
newVal = newVal <= minHeight?minHeight:newVal
boxHeight.setValue(newVal)
},
}
)).current;
return (
<Container>
<Animated.View style={[{ borderBottomColor: '#ccc', borderBottomWidth: 3 }, { height:boxHeight }]}>
<FlatList
data={orders}
renderItem={({ item, index }) => <List item={item} index={index} />}
/>
<View {...panResponder.panHandlers} style={{backgroundColor:'#000',height:20}}></View >
</Animated.View>
<ChatScreen />
</Container>
)
}
Pan-gesture- handler
const Chat = (props) => {
const orders = [
//some array
]
const Height = Dimensions.get('window').height
const boxHeight = useRef(new Animated.Value(Height / 3)).current
const minHeight = 10
const maxHeight = Height - 100
const onPanGestureEvent = (event) => {
boxHeight.setValue(
Animated.diffClamp(
Animated.add(
orderBoxHeight, Animated.multiply(
event.nativeEvent.translationY, Animated.diffClamp(
event.nativeEvent.velocityY, 0, 1
)
)
)
), minHeight, maxHeight
)
}
return (
<Container>
<Animated.View style={[{ borderBottomColor: '#ccc', borderBottomWidth: 3 }, { height: boxHeight }]}>
<FlatList
data={orders}
renderItem={({ item, index }) => <OrderList item={item} index={index} />}
/>
<PanGestureHandler
onGestureEvent={(event) => onPanGestureEvent(event)}
>
<Animated.View style={{ backgroundColor: '#000', height: 20 }}></Animated.View >
</PanGestureHandler>
</Animated.View>
<ChatScreen />
</Container>
)
}
But none Worked... I have also tried few other variations of both but none of them worked as desired either it slides too fast or does not move at all

React Native: Why is this useRef hook not auto scrolling

I am trying to auto-scroll between four images using the useRef hook which identifies the target using a state value called 'selected'.
I am getting unexpected behaviour in that the auto-scroll is erratic, usually leaving out the third image it should be scrolling to.
const [selected, setSelected] = useState(0);
const scrollRef = useRef(null);
const setSelectedIndex = e => {
const viewSize = e.nativeEvent.layoutMeasurement.width;
const contentOffset = e.nativeEvent.contentOffset.x;
const selectedIndex = Math.floor(contentOffset / viewSize);
setSelected(selectedIndex);
}
useEffect(() => {
setInterval(() => {
setSelected(prev => prev + 1);
scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * selected
});
}, 10000);
}, [selected]);
return (
<View style={{ height: '100%', width: '100%' }}>
<ScrollView
horizontal
pagingEnabled
onMomentumScrollEnd={setSelectedIndex}
ref={scrollRef}
>
{images.map(image => (
<Image
key={image}
source={image}
style={styles.backgroundImage}
/>
))}
</ScrollView>
<View style={styles.circleDiv}>
{
images.map((img, i) => (
<View
key={img}
style={[styles.whiteCircle, { opacity: i === selected ? 0.5 : 1 }]}
/>
))
}
</View>
</View>
);
If it helps, I have built it using componentDidMount which works as expected.
scrollRef = createRef();
componentDidMount = () => {
setInterval(() => {
this.setState({
prev => ({ selected: prev.selected + 1 }),
() => {
this.scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * this.state.selected
});
}
});
}, 5000);
}
try like this,
use useRef() instead of createRef()
return new state from setSelected arrow function
use setTimeout instead of setInterval
const scrollRef = useRef()
useEffect(() => {
setTimeout(() => {
setSelected((prev) => (
prev == slidesArray.length - 1 ? 0 : prev + 1
))
if (scrollRef.current) {
scrollRef.current.scrollTo({
animated: true,
y: 0,
x: DEVICE_WIDTH * (selected +1)
});
}
}, 5000);
}, [selected])