Check out a free preview of the full Intermediate React Native course

The "Gestures" Lesson is part of the full, Intermediate React Native course featured in this preview video. Here's what you'd learn in this lesson:

Kadi demonstrates using Reanimated 2 together with react-native-gesture-handler to implement swipe-to-delete UI for the mood history. Wrapping the whole MoodItemRow component in a PanGestureHandler allows tracking gesture events within a designated area.


Transcript from the "Gestures" Lesson

>> Now most animations actually aren't standalone most of the time we're going to be using animations in conjunction with gestures. So an example of this is when you have a scroll view and as you scroll up the header changes or for example if the user swipes and then as you swipe the UI is to change.

So for us what we want to do is you want to implement swipe to delete. So at the moment you need to tap on Delete button but most applications that you use for example Gmail. We'll have a functionality to swipe to delete as well. So we're going to add a pretty simple implementation so that the user will swipe right or left and when they go over a threshold the card will animate all the way to the, most are left and then disappear.

So to do that let's install react-native-gesture-handler. And then in your index.js, so this is at the root level, let's add import "react-native-gesture-handler" So this will be the very first line in your file to do an audit anywhere in the bottom. And we'll see the iOS and pod install.

And then for Android, we'll also need to update the main activity, that Java. So, that it overrides the method responsible for like. Basically, you need to hit add this, in order for the gesture handler to work on Android. So, let's copy this, and open main activity. And we'll add these imports.

And also we'll need to add this function. And then we should be good to go. So we'll need to, yarn ios to rebuild the iOS app. All right, let's open our MoodItemRow and in here. Let's import PanGestureHandler. From react-native-gesture-handler. Now pan is like a swipe interaction. You can also have like for example a PinchGestureHandler, And then let's wrap our entire moodItem in the PanGestureHandler Now you also want to set the minDeltaX and minDeltaY and this is basically to tell the gesture handler when to start registering the gesture event.

So, this basically tells us that we don't really care about gestures that go this way. We only really care about gestures that go this way, then, We use onGestureEvent. And this will be a function. And so just to show you what this looks like, evt.nativeEvent.y. So if I do console.warn Then if I go right or left, I think I care about x instead.

There you go. So as I go right or left, you can see the logs here updating. Now, we're actually not going to use this event directly, because we want to combine it with reanimated two. So there is a animatedGestureHandler. That's built into reanimated, which works in combination with the react-native-gesture-handler and creates like an optimized workflow.

So if we do const onGestureEvent, And we'll do, useAnimatedGestureHandler. And then this takes an object and this has a dependency array and this takes an object. So on active so this gets cold whenever you change, whenever you swipe, and this takes the event. And we'll do console.warn(event.x). So there's that if we use the gesture event directly, we could do evt.nativeEvent.x.

But with the animated gesture handler, we don't need to select the native events separately. So let's pass this here. All right so now when we swipe we get this error, saying onGestureHandler event is expected function got by your object type. This is actually because the first item inside the gesture handler has to be an animated or reanimated component.

So, if we import reanimated from react-native-reanimated, and then here on this view that's just inside the panGestureHandler we do reanimated.view. So both view and text can be created like this reanimated.view Oops, so now when we reload, then we go here, we get our look like we were expecting.

All right, so what we're interested in is the translation. So how much the user has moved the item from when they started. So on this event, we have, translationx, so let's see how this works, okay, so here when I start you can see it's like minus values, when I go to the right is plus values.

Perfect, and then we want to use this translation to actually animate this card so that when I move this with my finger, then the card moves with it. So let's create a shared value. So translatex=useSharedValue. And we'll start with 0. And here we'll do on active translatex.value=event.translationx. Did I misspell translate, translatex, excellent.

And now we will create an animated style, so const CardStyle=useAnimatedStyle. And this will be an arrow function that returns an object with a dependences array. And that's translate the card based on this translation value. So, let's do transform with an array with one object and translatex and this will be to translatex.value.

[COUGH] Now we can apply the cardStyle to our moodItem. Now, when we swipe on this card, you can see that the card moves, however it doesn't go back to his original position. So here in our useGestureEvent. We can do onEnd, and we'll get the evet, we don't need evet.

OnEnd, we will basically do translatex.value=0. So when we're finished we'll go back to 0. So there you go, so I go back here and it goes back to 0. Now it is snaps but thankfully we already have a nice tool to make any of these values animate. So if we use withTiming(0) and save, so now need to reload as well.

So now when we let go, you can see that it smoothly transitions back to 0. Now I want to do is when the user swipes past the certain threshold, we animates the card all the way to the right all the way to the left of the screen and then we delete the item.

So let's define a threshold, lets say a const.maxSwipe and let's make it 80. All right, so here onActive, so when we finish, we want to, I think we get the event here. And we can do if event.translationX, okay, so once the absolute value of this. So math.apps of translation x, so if this is larger than our maxSwipe.

So if this is true. Then we're going to delete, so let me just check that we're getting here correctly. Okay, and then if I just do a little bit, okay, cool so just works. Okay, so if we're at maxSwipe then we wanna delete so firstly what we want to do is you wanna swipe the card all the way to the like off the screen.

So translatex.value=1,000, so let's do withTiming and 1,000. Okay, let's try that. So we swipe here. Let's reload again, if you update these hooks they don't have to be reloads properly so it's good to do a proper refresh. Okay, so we'll go here, okay, so it's just because we're setting this to 0 so let's do else.

This one, okay, so reload. Go to here, all right, so we're at threshold. Okay, so this is not, wait, I did 100, let's do 1000. Okay, so we go here, okay, so it gets it does go off the screen, but it goes the wrong way. So it always goes on the right.

So we want to make this minus 1000. If they use a swipe to the left, and actually a nice way to do that is you can use math.sign. So I think it's a multiplied, so math.sign of this. Let me check if this works. Okay, so it's not a sign but I accidentally need didn't put it inside here.

So it's 1000*math.sign with the translation. So let me just reload this. So you can see that if I go to the left, it's forced to the left, if we go to the right sorts to the right. So what math.sign does is it looks at whatever sign this is and then it just returns basically plus one or minus one.

So if this is negative, it will make this whole value negative. And if it's positive, it stays positive. All right, so we've now swiped our item off the screen. So now we also wanna delete it. So we already have a context here. You will have already have handleDELETE here, so let's just call handleDELETE.

But the thing is we don't want to delete it straightaway we want to leave some time for the animation to finish and then delete it. So let's create another use callback DeleteWithDelay and react.useCallback Okay, let's just do a setTimeout. So here, let's do a setTimeout and our function and let's just wait for half a second, it's probably enough.

And after this timeout, let's just call handleDELETE. So we'll add the dependency array delete with delay. Okay and let's open it up, Then if we swipe. Okay, so now, we actually get this error. And you'll see this a lot when you're working with the animator tool, if you're trying to call JavaScript from within an animation.

So if you want to call a JavaScript code within this gesture handler like we do, you need to use runOnJS to tell it to run a JavaScript thread. So if you import runOnJS, and then here are deleteWithDelay, we wrap it in runOnJS. And save and also reload. And now when we swipe, you can see that it swipes away.

Our items get to be toggled. There you go. So I do realize that this is different from my code example. I'll probably update the code example later. So this is actually a simpler way of doing this. The original onGestureEvent that I built wasn't using the animated gesture handler.

So this is a much more optimized way of doing it rather than storing this in context.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now