How to solve blink image in react-native-snap-carousel? - react-native

How to solve blink image when back to first item in react-native-snap-carousel ? I try to look for many examples but fail all.
This is my script :
renderSlider ({item, index}) {
return (
<View style={styles.slide}>
<Image source={{uri: item.cover}} style={styles.imageSlider} />
</View>
);
}
<Carousel
ref={(c) => { this._slider1Ref = c; }}
data={data}
renderItem={this.renderSlider}
sliderWidth={width}
itemWidth={(width - 30)}
itemWidth={(width - 30)}
inactiveSlideScale={0.96}
inactiveSlideOpacity={1}
firstItem={0}
enableMomentum={false}
lockScrollWhileSnapping={false}
loop={true}
loopClonesPerSide={100}
autoplay={true}
activeSlideOffset={50}
/>
the comple documentation you can find here and about the plugin api you can find here.
Please anyone help me.
Thanks.

I had the same issue when loop={true} was set.
We came up with this workaround:
We maintained the activeSlide value in a state, and created a reference of Carousel refCarousel.
const [activeSlide, setActiveSlide] = useState(0);
const refCarousel = useRef();
Then we added code in useEffect to manually move the carousel item to the first one back when it reaches the end with a delay of 3500 milliseconds which is also set to autoplayInterval props.
This way, we achieved the looping effect.
useEffect(() => {
if (activeSlide === data.length - 1) {
setTimeout(() => {
refCarousel.current.snapToItem(0);
}, 3500)
}
}, [activeSlide]);
Below is the Carousel component declaration. Only the relevant props are shown here.
<Carousel
ref={refCarousel}
...
//loop={true}
autoplay={true}
autoplayDelay={500}
autoplayInterval={3500}
onSnapToItem={(index) => setActiveSlide(index)}
/>

use React Native Fast Image if you are facing blinking issue.

Related

react-native-snap-carousel is very laggy for large data

I am using react-native-snap-carousel to swipe through images. When there is like 0-10 images it's working fine, but otherwise it's very laggy. I tried the optimization methods but didn't fix it.
Here is my implementation (selectedItems is the data I have):
const renderItem = useCallback(
({ item, index }) => {
return (
<CarouselImage
ad={ad}
item={item}
index={index}
showImage={showImage}
/>
);
},
[ad, showImage]);
return ad?.videos?.length > 0 || ad?.images?.length > 0 ? (
<View style={styles.container}>
<Carousel
initialNumToRender={selectedItems.length}
maxToRenderPerBatch={5}
ref={carouselRef}
swipeThreshold={5}
itemWidth={wp(375)}
data={selectedItems}
sliderWidth={wp(375)}
enableMomentum={false}
lockScrollWhileSnapping
renderItem={renderItem}
onSnapToItem={(index) => setActiveSlide(index)}
/>
<Pagination
activeOpacity={1}
tappableDots={true}
animatedDuration={100}
inactiveDotScale={0.4}
inactiveDotOpacity={0.4}
carouselRef={carouselRef}
dotStyle={styles.dotStyle}
activeDotIndex={activeSlide}
dotsLength={selectedItems.length}
containerStyle={styles.pagination}
dotContainerStyle={styles.dotContainer}
inactiveDotStyle={styles.inactiveDotStyle}
/>
</View>
Is there something I am missing. Also, is there an alternative library that runs better with large data ?
Try this alternative library: react-native-banner-carousel-updated
I'm using this with more than 20 images and it's works fine.
I'm assuming that the optimization you've tried is what is described in the react-native-snap-carousel library docs...
I too found that every swipe was causing my screen's component to re-render.
You might be thinking...
I think it has to do with the state I am updating each time I scroll the component is re-rendered. Do you have ant idea how to solve this ?
To prevent the re-rendering of your <Carousel ... /> component, the optimization that you want to look at is to utilize React.memo()
Try refactoring your <Carousel ... /> component to a new component file, something like this...
Gallery.js
import React from "react";
import CarouselCardItem, { SLIDER_WIDTH } from "./CarouselCardItem";
import { ALL_RECIPES } from "../config/Recipe/allRecipes";
import Carousel from "react-native-snap-carousel";
const Gallery = ({ carouselRef, selectedItems, setActiveSlide}) => {
console.log("render"); // <== This will render only when props change (ie. the ref - which should not change, or you pass fresh data - in which case you want it to re-render)
return (
<Carousel
initialNumToRender={selectedItems.length}
maxToRenderPerBatch={5}
ref={carouselRef}
swipeThreshold={5}
itemWidth={wp(375)}
data={selectedItems} // <== selectedItems could be passed in as prop, or from app state
sliderWidth={wp(375)}
enableMomentum={false}
lockScrollWhileSnapping
renderItem={renderItem}
onSnapToItem={(index) => setActiveSlide(index)}
/>
);
};
const GalleryMemo = React.memo(Gallery);
export default GalleryMemo;
Then in your screen file, use it
<GalleryMemo carouselRef={carouselRef} selectedItems={selectedItems} setActiveSlide={setActiveSlide}/>

KeyboardAwareScrollView props innerRef scrollToEnd not working

I'm trying to call scrollToEnd() in a screen that uses https://github.com/APSL/react-native-keyboard-aware-scroll-view and I get the error:
"cannot read property 'props' of undefined".
My code looks like this:
let scroll
(at the beginning of the file)
Then, inside the return:
<KeyboardAwareScrollView innerRef={ref => { scroll = ref }}>
my scrollable code
</KeyboardAwareScrollView>
And then there is a button which has
onPress={() => scroll.props.scrolltoEnd()}
Clicking the button gives the error above, which makes me think I'm not using innerRef correctly? Do I need to use useRef instead at some point? Any help is appreciated, thanks!
on KeyboardAwareScrollView use React.useRef
const scrollRef = React.useRef(null);
<KeyboardAwareScrollView
ref={scrollRef}
....
And on your input, listen onContentSizeChange
<Input
onContentSizeChange={(e) => {
if (scrollRef && scrollRef.current) {
scrollRef.current?.scrollToEnd();
}
}}
/>
You must use innerRef instead of ref and it will work.
<KeyboardAwareScrollView
innerRef={(ref) => {
scrollRef.current = ref;
}}

Change videosource in React Native

Im using react-native-video in my react-native application. I want to be able to dynamically change videosource but found out it wasnt that easy. My approach is simply by changing the clip name with a hook, changing video1 to video2. But I was not able to update the videoinstance:
I did try something like this:
const [clipSelected, setClipSelected] = useState('video1');
const onButton = (name) => {
console.log("videoname", name)
setClipSelected(name);
}
return (
<Fragment>
<View style={styles.container}>
<Video
source={require('./' + clipSelected + '.mp4')}
ref={(ref) => {
bgVideo = ref
}}
onBuffer={this.onBuffer}
onError={this.videoError}
rate={1}
repeat={true}
/>
<Button onPress={(e) => onButton('video2')}></Button>
</View>
</Fragment >
);
Are there any other library, approach or method anyone are aware of where I can solve this? Basically a way to update the source instance of the video. Im going to run this on an Android TV ...
Use the status values to make changes.
const [clipSelected, setClipSelected] = useState(false);
const onButton = () => {
setClipSelected(true);
}
...
<Video
source={clipSelected === false ? require('./video1.mp4') : require('./video2.mp4')}
...
<Button onPress={(e) => onButton()}></Button>

FlatList onEndReached called On Load (React Native)

When I use onEndReached function in FlatList, it gets called automatically.
Below is the link of this issue.
Link
Is there a solution available for it or any alternative in iOS?
Edited:
Below is the code I tried but this doesn't seems to work.
constructor(props){
super(props);
this.state = {
flatListReady:false
}
}
loadMore(){
if(!this.state.flatListReady){
return null;
}
else{
alert("End Reached")
}
}
_scrolled(){
this.setState({flatListReady:true});
}
render() {
return (
<Layout style={{ flex: 1 }}>
<FlatList
data={listData}
renderItem={({item}) => this._renderItem(item)}
keyExtractor={(item, index) => item.key}
onEndReached={() => this.loadMore()}
onEndReachedThreshold={0.5}
onScroll={() => this._scrolled()}
/>
</Layout>
Try this,
onEndReachedThreshold={0.5}
onEndReached={({ distanceFromEnd }) => {
if(distanceFromEnd >= 0) {
//Call pagination function
}
}}
Sometimes things don't work like they are supposed to, at the end of the day it's not native code where, so may the order of your components or the fact that the Flatlist is encapsulated in a component that is not intended to be, or there is some property should be passed to the Flatlist component itself to activate the onEndReached callback properly.
I've faced this myself, and I didn't know what to do to make it work properly.
A beautiful workaround is derived from the fact the Flatlist inherits ScorllView properties. so you could use the onScroll property to detect if the end has reached or not.
<FlatList
data={this.props.dashboard.toPreviewComplaints}
onScroll={({nativeEvent})=>{
//console.log(nativeEvent);
if(!this.scrollToEndNotified && this.isCloseToBottom(nativeEvent)){
this.scrollToEndNotified = true;
this.loadMoreData();
}
}}
/>
this.scrollToEndNotified is used as a flag not to abuse the call to the loadMore endpoint
isCloseToBottom({layoutMeasurement, contentOffset, contentSize}){
return layoutMeasurement.height + contentOffset.y >= contentSize.height - 100;
}
So whenever it succeed in the isCloseToBottom call it means that you have reached the end of the list, so you can call the loadMoreData function
handle this function very carefully,
endReached=()=>{
//take care of ES6 Fat arrow function and trigger your conditions properly with current state and new state data or current state with new Props.
Based on those conditions only, you need to trigger the other API call
}
<FlatList data={this.state.data}
extraData={this.state.load}
renderItem={this.renderCard}
keyExtractor={item => item.fundRequestId}
onEndReached={this.endReached}
onEndReachedThreshold={.7}
ListFooterComponent={this.renderFooter}
/>

Prevent Flatlist from scrolling when new items are added

When adding data to the Flatlist (e.g. subscriptions) it scrolls down leading to a very bad UX.
Any idea on how this could be solved?
Actually, I think this must be handled in native level, but not handled yet,
I solve my problem by saving scroll offset and set it again after reloading data like this:
reloadData(flatListData){
this.setState({
flatListData: flatListData
});
requestAnimationFrame(() => {
this.flatList.scrollToOffset({
animated: false,
offset: this.flatListLastOffset
});
});
}
...
<FlatList
data={this.state.flatListData}
ref={ref => this.flatList = ref}
onScroll={(event: Object) => {
this.flatListLastOffset = event.nativeEvent.contentOffset.y;
}}
horizontal={false}
scrollEventThrottle={16}
/>
this is not the best solution, but can fix my problem for now