I am using a a carousel library (that uses a FlatList) to drag things in React Native.
I've got this for the carousel:
import * as React from "react";
import Carousel from "react-native-reanimated-carousel";
import Card from "components/cards/Card";
function CardCarousel(props) {
return (
<Carousel
scrollAnimationDuration={200}
width={300}
windowSize={9}
moveSize={5}
style={{
width: 2000,
height: 300,
}}
data={props.articleGroup.data}
renderItem={(article, index) => (
<Card
article={article.item}
key={index "-main-" props.articleGroup.category}
/>
)}
/>
);
}
export default CardCarousel;
And then my card looks like this:
const Card = (props) => {
const [bookmark, setBookmark] = useState(props.article.bookmark);
const cdnURL = wrapImgCDN(props.article.image, 400);
const routeChange = async (event) => {
... code to change route
};
const _bookmarkToggle = () => {
bookmarkToggle(props.article);
setBookmark(!bookmark);
};
return (
<Pressable
onPress={routeChange}
>
<WhiteCard
{...props}
_bookmarkToggle={_bookmarkToggle}
cdnURL={cdnURL}
bookmark={bookmark}
/>
</Pressable>
);
};
When I am dragging the FlatList, if I do so by grabbing onto one of the <Card />
items, it clicks and changes the route to the given card, but I don't want to allow a user to click into a card unless the scrolling is stopped/the user didn't drag at all.
How can I do that?
CodePudding user response:
Since you are wrapping the rendered items of the carousel
into a Pressable
and fire the routeChange
in its onPress
function, it will change the route as soon as you press one of the items in the carousel.
We could use the onSnapToItem
callback function of the react-native-reanimated-carousel component instead. This function is passed as a prop to the component and will be called as soon as an item in the FlatList 'snaps'. It receives the index of the item that will be snapped. We can compare this with the current item and whenever it changes, we pass a boolean prop to the Card
component.
The current index
can be accessed using ref
via the getCurrentIndex
function as usual.
Notice that this needs an additional state. However, we only change the state if the item has actually changed, that is, a new item snapped into position
.
The boolean state will be true for the current snapped item, and false for the other ones. We react on fireRouteChange
prop changes in a useEffect
in the Card
component.
function CardCarousel(props) {
const ref = React.useRef(null);
const [currentIndex, setCurrentIndex] = React.useState(0);
return (
<Carousel
ref={ref}
onSnapToItem={(index) => index !== ref.current.getCurrentIndex() && setCurrentIndex(index)}
scrollAnimationDuration={200}
width={300}
windowSize={9}
moveSize={5}
style={{
width: 2000,
height: 300,
}}
data={props.articleGroup.data}
renderItem={(article, index) => (
<Card
article={article.item}
fireRouteChange={currentIndex === index}
key={index "-main-" props.articleGroup.category}
/>
)}
/>
);
}
const Card = (props) => {
const [bookmark, setBookmark] = useState(props.article.bookmark);
const cdnURL = wrapImgCDN(props.article.image, 400);
React.useEffect(() => {
if(props.fireRouteChange) {
... code to change route
}
}, [props.fireRouteChange])
const _bookmarkToggle = () => {
bookmarkToggle(props.article);
setBookmark(!bookmark);
};
return (
<WhiteCard
{...props}
_bookmarkToggle={_bookmarkToggle}
cdnURL={cdnURL}
bookmark={bookmark}
/>
);
};
Assumption: The routeChange
function can be located in CardCarousel
We can make this simpler if we assume that the routeChange
function can be located in CardCarousel
. I do not have access to the implementation details of this function, thus I include this here by making this assumption.
function CardCarousel(props) {
const ref = React.useRef(null);
function routeChange(index) {
... code to change route of the current snapped index
};
return (
<Carousel
ref={ref}
onSnapToItem={(index) => index !== ref.current.getCurrentIndex() && fireRouteChange(index)}
scrollAnimationDuration={200}
width={300}
windowSize={9}
moveSize={5}
style={{
width: 2000,
height: 300,
}}
data={props.articleGroup.data}
renderItem={(article, index) => (
<Card
article={article.item}
fireRouteChange={currentIndex === index}
key={index "-main-" props.articleGroup.category}
/>
)}
/>
);
}
const Card = (props) => {
const [bookmark, setBookmark] = useState(props.article.bookmark);
const cdnURL = wrapImgCDN(props.article.image, 400);
const _bookmarkToggle = () => {
bookmarkToggle(props.article);
setBookmark(!bookmark);
};
return (
<WhiteCard
{...props}
_bookmarkToggle={_bookmarkToggle}
cdnURL={cdnURL}
bookmark={bookmark}
/>
);
};
Disable the onPress
function while the user is scrolling
If you want to disable the onPress
function while the user is scrolling and only allow it while the carousel is snapped in place, you can use a state and the onScrollBegin
and onScollEnd
functions.
function CardCarousel(props) {
const [allowRouteChange, setAllowRouteChange] = useState(true)
return (
<Carousel
scrollAnimationDuration={200}
width={300}
windowSize={9}
moveSize={5}
onScrollBegin={() => setAllowRouteChange(false)}
onScrollEnd={() => setAllowRouteChange(true)}
style={{
width: 2000,
height: 300,
}}
data={props.articleGroup.data}
renderItem={(article, index) => (
<Card
allowRouteChange={allowRouteChange}
article={article.item}
key={index "-main-" props.articleGroup.category}
/>
)}
/>
);
}
const Card = (props) => {
const [bookmark, setBookmark] = useState(props.article.bookmark);
const cdnURL = wrapImgCDN(props.article.image, 400);
const routeChange = async (event) => {
if (props.allowRouteChange) {
... code to change route
}
};
const _bookmarkToggle = () => {
bookmarkToggle(props.article);
setBookmark(!bookmark);
};
return (
<Pressable
onPress={routeChange}
>
<WhiteCard
{...props}
_bookmarkToggle={_bookmarkToggle}
cdnURL={cdnURL}
bookmark={bookmark}
/>
</Pressable>
);
};