Implementing swipe-right on a React Native FlatList
August 07, 2017
6 minute read
I’m progressing on my “master-detail” pattern for a react-native app. The actual implementation of master-detail is shockingly simple (more on that later). However, I bumped into some specific issues when I was implementing it. The first of these was covered last time – how to detect orientation changes in React Native. The next is this. How do I implement swipe-right so that I can add a swipe-to-delete function to a FlatList.
Let me explain a little further. The latest edition of React Native has updated the list handling. ListView is (or will be) deprecated. A bunch of new list handling methods come in, among them the FlatList, SectionList and VirtualizedList. This is great. I no longer have to implement a data source. All I have to do is pass the data I want to render to the FlatList and a rendering function and I am done.
There is also another component – the Swipeout – that implements swiping. However, it does it for ListView, which is deprecated. So no joy there. Or so I thought. It’s actually possible, but with a huge set of provisos.
Let’s start with the setup. I have two components – a NoteList.js component that renders the list, and a NoteListItem.js that renders the NoteListItem. I want a few events to fire on the NoteList.js:
onSelectItem(item) will fire when the item is pressed.
onDeleteItem(item) will fire when the item is swipe-right deleted.
onAddItem() will fire when the plus item in the status bar is pressed.
These will then effect a scene change or a data update as needed. There are a couple of rules to keep in mind:
Touchable events must have a native item as their direct child (or pass on setNativeProps).
Swipeout requires the rowId to handle open/close properly (so only one swipe drawer is open at any given time).
The first rule means you need to handle an onPress event in the NoteListItem renderer which is then picked up by the list item. Here is my NoteListItem.js script:
Note how I pass in the onPress event handler (with a default, in case you don’t do this). I also wrap the View (which is a native component) in the TouchableHighlight so that my entire view is clickable. This makes the entire row clickable if we define an onPress event handler.
Let’s take a look at the setup of the NoteList.js class first.
This is all fairly basic FlatList code. If you use a simple application, like this:
Then you should be able to see the three items and click on them. A debug message will appear in the debug console. In addition, you can click on the plus sign in the status bar and get the onAddItem debug message.
Now, let’s consider swiping. All this work is done in the NoteList.js component. I’m going to use react-native-swipeout to handle swiping. Firstly, I need to understand what row is currently showing the swipe-right drawer. To do this, add an activeRow element to the component state:
Next, ensure that the FlatList is re-rendered when the state is changed. This is handled in the FlatList props:
This state is changed on two occasions. Firstly, when the user swipes right, I’m going to call event handler this.onSwipeOpen(). Then, when the swipe-right drawer closes, I’m going to call this.onSwipeClose(). I’ll use these methods to control the state:
The important functionality is within the onSwipeClose() method. This will only reset the state to null (meaning nothing is selected) when the closed item is the currently active element. This is important because Swipeout calls the onClose event handler (which is this method) for all sorts of events, most of which do not actually indicate a new swipe.
Finally, let’s take a look at how this is rendered in the renderItem() method:
The main item to note here is that the rowId is filled in by the index of the data object passed to FlatList, which starts at 0 and goes up from there. The onOpen() and onClose() event handlers are wired to the new onSwipeOpen() and onSwipeClose() methods, and the close flag is set so that the drawer is closed if the active row does not match.
If you implement this code, you will note the following:
Pressing a row calls onSelectItem().
Swiping right will open the swipe-right drawer with the Delete button in it.
Swiping right on another row will cause the original swipe-right drawer to close.
Pressing the delete button calls onDeleteItem().
Hopefully, the next blog post will be about the master-detail pattern in React Native. Until then, I hope this helps implementing awesome lists.
[AWS AppSync] is a managed [GraphQL] service that can (and probably should) act as the data layer for your app. I’m not going to go into the details of how to configure it since I’ve gone through that in excruciating detail recently (see blog #1, #2, #3, and #4). Rather, I want to take a look at how you can send a query to AWS AppSync from your React (or React Native) app.
I am currently developing a “restaurant reviews” app, written in React Native and using a suite of services surrounding [AWS AppSync] for the data backend. Yesterday, I ran into a problem. This is how I solved that problem.