I was trying to find any tutorial about the implementation of react-native-snap-carousel example but I couldnt find any. If any of you have seen any tutorials on this, please suggest a link.
I have been through its documentation and examples provided by the creators of it, however they look very complicated to me.
you have to provide data and it will do automatically mapping and assign functional component to render item it will return json format with index and item you can see below example
const container = ({edges,_loadmore,navigation}) =>{
const _renderItem = ({ item, index }) => {
return (
<View>
<Render_component />
</View>
);
};
return (
<Carousel
onSnapToItem={slideIndex => {
setcurrentSlider(slideIndex);
_loadMore();
}}
onBeforeSnapToItem={slideIndex => {
setBeforeSlider(slideIndex);
}}
inactiveSlideOpacity={1}
inactiveSlideScale={1}
enableSnap
ref={carRef}
data={edges}
renderItem={_renderItem}
sliderHeight={screenHeight}
itemHeight={screenHeight}
vertical={true}
/>
)
}
Related
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}/>
I'm working on a flatlist that has complex child that cause expensive rerender, I need to optimize that but I'm not able to stop the rerendering with useMemo, please help me to go through this.
Here my list code:
<FlatList
data={thePosts}
extraData={thePosts}
keyExtractor={(item, index) => index.toString()}
removeClippedSubviews={true}
maxToRenderPerBatch={5}
updateCellsBatchingPeriod={30}
initialNumToRender={11}
windowSize={5}
refreshing={isRefreshing}
onRefresh={handleOnRefresh}
onEndReached={isLoading ? () => null : () => getPosts("more")}
onEndReachedThreshold={0.1}
renderItem={memoizedPost}
//renderItem={renderThePost}
ItemSeparatorComponent={renderThePostSep}
ListFooterComponent={renderThePostListFooter}
/>
here the renderPost:
const renderThePost = (props) => {
let post = props.item;
if (post[0].type == "share") {
return (
<TheSharedPost thePost={post} />
);
} else {
return <ThePost thePost={post} />;
}
};
I've tried to use memoization like this:
const memoizedPost = useMemo(() => renderThePost, []);
Now the problem is, the empty array as useMemo argument I think that only accept the first render but not working, I've tried to use [item.someProperty] but I'm not able to recognize item in the argument (item is not defined)
I've also used useCallback but still no luck, a lot o rerendering happen. Please help me to fix this. Tnz
you can use React.memo to avoid rendering of flatlist items
function TheSharedPost(props) {
/* render using props */
}
export default React.memo(TheSharedPost);
function ThePost(props) {
/* render using props */
}
export default React.memo(ThePost);
In the following code, I expect OfferList to rerender when I add an offer item to the store. OfferList itself is not an observable, but the offer array is passed as a prop.
export const MerchantScreen: FC = observer(() => {
const { merchantStore } = useStores()
return (
<View>
<OfferList data={merchantStore.offers} />
<View>
<Button title={"New Offer"} onPress={() => merchantStore.addOffer()}/>
</View>
</View>
)
})
export const OfferList: FC<OfferListProps> = ({ data }: OfferListProps) => {
const renderItem = (offer: ListRenderItemInfo<any>) => {
return (
<Text>{offer.name}</Text>
)
}
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
)
}
I use Mobx State Tree. All merchantStore.addOffer() does for now is push another offer item into the array.
What I tried / findings:
When I read from the store in my MerchantScreen, e.g. by adding
<Text>{ merchantStore.offers.toString() }</Text>
, the OfferList will also update. I suspect that reading from the store directly in the parent component will force a rerender of the child component as well.
I stumbled upon some answers here that would indicate that a missing key attribute within the FlatList renderItems could be the issue. Tried using key={item.id} to no avail. Also, as you can see I use the keyExtractor prop of FlatList.
Another answers suggested introducing local state to the component like this:
export const OfferList: FC<OfferListProps> = ({ data }: OfferListProps) => {
const [offers, setOfferss] = useState()
useEffect(() => {
setOffers(data)
}, [data])
const renderItem = (offer: ListRenderItemInfo<any>) => {
return (
<Text>{offer.name}</Text>
)
}
return (
<FlatList
data={offers}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
)
}
This does not work and my gutfeeling is that this is not how it's done.
As you see my MerchantScreen parent component is an observable while my child component OfferList is not. From my expectation, the observer on my parent component should be enough. The parent component should already detect the change in the store and rerender. The child component in itself does not even use stores.
Overall, the problem at hand seems quite trivial so I guess I am just missing out on an important detail.
MobX only tracks data accessed for observer components if they are directly accessed by render, so if you want to react to each of the offers you need to access them somewhere. You sort of did when you tried merchantStore.offers.toString(), that's why it worked.
So first of all you need to make OfferList an observer.
But then you have FlatList which is native component and you can't make it an observer. What you can do is to access each offers item inside OfferList (just to subscribe for updates basically) like that data={offers.slice()} or even better with MobX helper method toJS data={toJS(offers)}
Depending on your use case you might also want to use <Observer> inside renderItem callback:
const renderItem = (offer: ListRenderItemInfo<any>) => {
return (
<Observer>{() => <Text>{offer.name}</Text>}</Observer>
)
}
Here is a simplified version of my code.
Notice the setFieldValue_ and this.setFieldValue_ = setFieldValue;
This code works fine, I'm able to get the output when submit button is clicked.
I'm actually wondering if this is the right way to do it? If not, can you point me to the right direction? Also what is this method called? (assigning class variable to some function and use it within another function)
class MyComponent extends React.Component {
setFieldValue_;
someFunction() {
this.setFieldValue_("name", value);
}
render() {
return (
<Formik
initialValues={{
something: ""
}}
onSubmit={values => console.log(values)}
>
{({
setFieldValue,
}) => {
this.setFieldValue_ = setFieldValue;
<ThirdPartyCustomComponent onChange={this.someFunction} />
}}
</Formik>
}
}
I would personally have the onChange simply call formik set field value there and then rather than using different functions. Strictly speaking you don't want to set the value like that because every re-render is setting the value again.
I would also recommend looking at custom formik inputs using the useField hook - https://jaredpalmer.com/formik/docs/api/useField. This will allow you to write a small wrapper around your third party component and formik. Noticing you have used a class based component you may want to do some short reading into react hooks before throwing yourself into using useField.
Docs example:
const MyTextField = ({ label, ...props }) => {
const [field, meta, helpers] = useField(props);
return (
<>
<label>
{label}
<input {...field} {...props} />
</label>
{meta.touched && meta.error ? (
<div className='error'>{meta.error}</div>
) : null}
</>
);
};
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.