Aujourd'hui, il est difficile de surprendre quiconque avec la possibilité de balayer les éléments de la liste dans les applications mobiles. L'une de nos applications natives réactives avait également une telle fonctionnalité, mais récemment, il était nécessaire de l'étendre avec la possibilité de glisser-déposer des éléments de liste. Et comme le processus de recherche d'une solution m'a coûté une certaine quantité de cellules nerveuses, j'ai décidé de déposer un court article afin de gagner un temps précieux pour les générations futures.

Dans notre application, nous avons utilisé le package react
react-native-swipe-list-view
pour créer la liste swipeable. La première pensée a été de prendre un paquet avec la fonctionnalité drag'n'drop et de traverser le hérisson avec un serpent.
Une recherche sur Internet a donné trois candidats:
react-native-draggable-list
react-native-draggable-flatlist
react-native-sortable-list
react-native-draggable-flatlist
.
En utilisant le premier package, il n'a pas été possible d'exécuter même l'exemple ci-joint (cependant, non seulement pour moi, le problème correspondant est indiqué dans les
problèmes ).
J'ai dû bricoler avec le deuxième paquet, mais j'ai réussi à créer une liste glissable et glissable. Cependant, le résultat n'a pas été inspiré - le composant clignotait sans vergogne: clignotement du redessin, chute d'éléments bien au-delà de la liste, ou même leur disparition. Il est devenu clair que sous cette forme, il ne peut pas être utilisé.
Au début, le dernier paquet s'est également comporté comme une dame capricieuse, mais il s'est avéré que je ne savais tout simplement pas comment le cuisiner. Ayant ramassé une clé au cœur de cette "dame", j'ai réussi à obtenir un résultat acceptable.
Dans notre projet, il y avait une liste glissable à laquelle vous devez fixer le glisser-déposer, mais en pratique, il est préférable de partir de l'autre bord: faites d'abord une liste glissable, puis ajoutez la possibilité de glisser.
Les lecteurs sont censés savoir comment créer un projet natif réactif, alors concentrons-nous sur la création de la liste dont nous avons besoin. L'exemple présenté ci-dessous est du code TypeScript.
Faire une liste glissable
Commençons donc par installer le package:
yarn add react-native-draggable-flatlist
Nous importons les modules nécessaires:
import React, { Component } from 'react' import { View } from 'react-native' import styles from './styles' import DraggableFlatList, { RenderItemInfo, OnMoveEndInfo } from 'react-native-draggable-flatlist' import ListItem from './components/ListItem' import fakeData from './fakeData.json'
Ici,
DraggableFlatList
est un composant du package installé qui implémente la fonction glisser-déposer,
ListItem
est notre composant pour afficher un élément de liste (le code sera présenté ci-dessous),
fakeData
est un fichier json qui contient de fausses données - dans ce cas, un tableau d'objets de la forme:
{"id": 0, "name": "JavaScript", "favorite": false}
Dans une application réelle, ces données proviendront très probablement de votre composant à partir d'accessoires ou seront téléchargées à partir du réseau, mais dans notre cas, nous gérerons avec peu de sang.
Puisque TypeScript est utilisé dans cet exemple, nous décrivons certaines entités:
type Language = { id: number, name: string, favorite: boolean, } interface AppProps {} interface AppState { data: Array<Language> }
Le type de
Language
nous indique quels champs les éléments de liste auront.
Dans cet exemple, nous n'obtiendrons rien des accessoires, donc l'interface
AppProps
est triviale, et dans l'histoire, nous allons stocker un tableau d'objets
Language
, qui est indiqué dans l'interface
AppState
.
Étant donné que le code du composant n'est pas très volumineux, je vais le donner dans son intégralité:
Code de composant d'application class App extends Component<AppProps, AppState> { constructor(props: AppProps) { super(props) this.state = { data: fakeData, } } onMoveEnd = ({ data }: OnMoveEndInfo<Language>) => { this.setState({ data: data ? [...data] : [] }) } render() { return ( <View style={styles.root}> <DraggableFlatList data={this.state.data} renderItem={this.renderItem} keyExtractor={(item) => item.id.toString()} scrollPercent={5} onMoveEnd={this.onMoveEnd} /> </View> ) } renderItem = ({ item, move, moveEnd, isActive }: RenderItemInfo<Language>) => { return ( <ListItem name={item.name} move={move} moveEnd={moveEnd} isActive={isActive} /> ) } }
La méthode
onMoveEnd
appelée lorsque l'élément est déplacé. Dans ce cas, nous devons mettre la liste avec le nouvel ordre des éléments dans l'état, nous appelons donc la méthode
this.setState
.
La méthode
renderItem
un élément de liste et accepte un objet de type RenderItemInfo <Language>. Cet objet comprend les champs suivants:
item
- l'élément suivant du tableau passé en tant que données à la liste,move
et moveEnd
sont des fonctions qui sont appelées lorsqu'un élément de liste est déplacé, le composant DraggableFlatList
fournit ces fonctions,isActive
est un champ booléen qui détermine si l'élément est actuellement déplaçable.
Le composant pour afficher l'élément de liste est, en fait,
TouchableOpacity
, qui, lorsqu'il est pressé pendant longtemps, appelle
move
, et lorsqu'il est relâché, il se
moveEnd
.
Code composant ListItem import React from 'react' import { Text, TouchableOpacity } from 'react-native' import styles from './styles' interface ListItemProps { name: string, move: () => void, moveEnd: () => void, isActive: boolean, } const ListItem = ({ name, move, moveEnd, isActive }: ListItemProps) => { return ( <TouchableOpacity style={[styles.root, isActive && styles.active]} onLongPress={move} onPressOut={moveEnd} > <Text style={styles.text}>{name}</Text> </TouchableOpacity> ) } export default ListItem
Les styles de tous les composants sont déplacés vers des fichiers séparés et ne sont pas indiqués ici, mais ils peuvent être affichés dans le
référentiel .
Le résultat résultant:

Ajoutez la possibilité de glisser
Eh bien, nous avons réussi à faire face à la première partie, nous passons à la deuxième partie du ballet Marlezon.
Pour ajouter la possibilité de faire glisser les éléments de la liste, nous utilisons le package
react-native-swipe-list-view
.
Pour commencer, installons-le:
yarn add react-native-swipe-list-view
Ce package contient le composant
SwipeRow
qui, selon la documentation, devrait inclure deux composants:
<SwipeRow> <View style={hiddenRowStyle} /> <View style={visibleRowStyle} /> </SwipeRow>
Notez que la première vue est dessinée sous la seconde.
Modifions le code du composant
ListItem
.
Code composant ListItem import React from 'react' import { Text, TouchableOpacity, View, Image } from 'react-native' import { SwipeRow } from 'react-native-swipe-list-view' import { Language } from '../../App' import styles from './styles' const heart = require('./icons8-heart-24.png') const filledHeart = require('./icons8-heart-24-filled.png') interface ListItemProps { item: Language, move: () => void, moveEnd: () => void, isActive: boolean, onHeartPress: () => void, } const ListItem = ({ item, move, moveEnd, isActive, onHeartPress }: ListItemProps) => { return ( <SwipeRow rightOpenValue={-180}> <View style={styles.hidden}> <TouchableOpacity onPress={onHeartPress}> <Image source={item.favorite ? filledHeart : heart} /> </TouchableOpacity> </View> <TouchableOpacity activeOpacity={1} style={[styles.root, isActive && styles.active]} onLongPress={move} onPressOut={moveEnd} > <Text style={styles.text}>{item.name}</Text> </TouchableOpacity> </SwipeRow> ) } export default ListItem
Tout d'abord, nous avons ajouté le composant
SwipeRow
avec la propriété
SwipeRow
, qui détermine la distance sur laquelle l'élément peut être glissé.
Deuxièmement, nous avons déplacé notre
TouchableOpacity
intérieur de
SwipeRow
et ajouté une vue, qui sera dessinée sous ce bouton.
Dans cette vue, une image est dessinée pour déterminer si la langue est préférée. Lorsque vous cliquez dessus, la valeur doit être inversée et, puisque les données se trouvent dans le composant parent, vous devez lancer ici un rappel qui exécute cette action.
Apportez les modifications nécessaires au composant parent:
Code de composant d'application import React, { Component } from 'react' import { View } from 'react-native' import styles from './styles' import DraggableFlatList, { RenderItemInfo, OnMoveEndInfo } from 'react-native-draggable-flatlist' import ListItem from './components/ListItem' import fakeData from './fakeData.json' export type Language = { id: number, name: string, favorite: boolean, } interface AppProps {} interface AppState { data: Array<Language> } class App extends Component<AppProps, AppState> { constructor(props: AppProps) { super(props) this.state = { data: fakeData, } } onMoveEnd = ({ data }: OnMoveEndInfo<Language>) => { this.setState({ data: data ? [...data] : [] }) } toggleFavorite = (value: Language) => { const data = this.state.data.map(item => ( item.id !== value.id ? item : { ...item, favorite: !item.favorite } )) this.setState({ data }) } render() { return ( <View style={styles.root}> <DraggableFlatList data={this.state.data} renderItem={this.renderItem} keyExtractor={(item) => item.id.toString()} scrollPercent={5} onMoveEnd={this.onMoveEnd} /> </View> ) } renderItem = ({ item, move, moveEnd, isActive }: RenderItemInfo<Language>) => { return ( <ListItem item={item} move={move} moveEnd={moveEnd} isActive={isActive} onHeartPress={() => this.toggleFavorite(item)} /> ) } } export default App
Sources du projet sur
GitHub .
Le résultat est présenté ci-dessous:
