Il est possible de construire un tableau d'objets JSX et de les afficher.
const items = [
<Text>A</Text>,
<Text>B</Text>,
<Text>C</Text>,
];
// ...
return (
<View style={{ flexDirection: 'row', justifyContent: 'space-around' }}>
{ items }
</View>
);
On peut également exploiter les fonctions des tableaux, pour traiter des données et produire un tableau
const items = ["a", "b", "c"];
// ...
return (
<View style={{ flexDirection: 'row', justifyContent: 'space-around' }}>
{
items.map((i, index) => {
return (
<Text
style={{
color: (index % 2 == 0 ? 'red' : 'blue')
}}
>
{ i }
</Text>
);
})
}
</View>
);
Attention
return (
<View style={{ flexDirection: 'row', gap: 16, justifyContent: 'center', flexWrap: 'wrap' }}>
{
[...Array(99)].map(() => {
return (
<Text
style={{
backgroundColor: 'lightgray',
padding: 16
}}
>
{ '🤓' }
</Text>
);
})
}
</View>
);
Each child in a list should have a unique "key" prop
Pour permettre à React de synchroniser efficacement l'état et le rendu, chaque item d'une liste doit être identifié par une clé unique via la prop
key
.<X key={ ... } />
La ScrollView permet de faire défiler le contenu s'il se retrouve à l'extérieur des limites de l'affichage.
Pour une petite quantité d'items, car le rendu est calculé pour tous les items en même temps!
import { ScrollView } from 'react-native';
// ATTENTION contentContainerStyle pour controler la mise en page dans la ScrollView
// Tester nowrap et horizontal={ true }
return (
<ScrollView contentContainerStyle={{ flexDirection: 'row', gap: 16, justifyContent: 'center', alignItems: 'center', flexWrap: 'wrap' }} >
{
[...Array(99)].map(() => {
return (
<Text
style={{
backgroundColor: 'lightgray',
padding: 16
}}
>
{ '🤓' }
</Text>
);
})
}
</ScrollView>
);
Plusieurs personnalisations disponibles
Il existe 2 mécanismes pour une gestion performante de l'affichage en liste via React Native. Ceux-ci utilisent la virtualisation pour créer un ensemble réduit de cellules à afficher et les réutiliser au cours du défilement.
const numbers = [...Array(99)];
return (
<FlatList
data={ numbers }
renderItem={ () => {
return (
<Text
style={{
backgroundColor: 'lightgray',
padding: 16,
textAlign: 'center'
}}
>
{ '🤓' }
</Text>
)
}}
/>
);
On peut personnaliser le comportement
function NumberItem({ value }) {
return (
<TouchableOpacity
onPress={ () => console.log(`${value} pressed!`) }
>
<Text
style={{
padding: 8,
backgroundColor: 'lightgray',
textAlign: 'center',
fontSize: 24
}}
>
{ `${value} 🤓` }
</Text>
</TouchableOpacity>
)
}
export default function App() {
const numbers = [...Array(99)].map((_, index) => index);
return (
<FlatList
data={ numbers }
renderItem={ (listItem) => <NumberItem value={ listItem.item } /> }
ItemSeparatorComponent={ () => <View style={{ height: 2, backgroundColor: 'black' }}/> }
/>
);
}
Intégrer le mécanisme de liste dans le projet de départ ci-dessous.
FlatList
[EXTRA] Tester la SectionList pour regrouper les items selon s'il sont complétés ou non
Projet de départ
/*
Dependencies
npx expo install react-native-safe-area-context react-native-root-toast expo-crypto
*/
import { useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Alert, View, ScrollView, TouchableHighlight, Switch, Button, Text, TextInput } from 'react-native';
import { randomUUID as uuid } from 'expo-crypto';
import { Ionicons } from '@expo/vector-icons';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { RootSiblingParent } from 'react-native-root-siblings';
import Toast from 'react-native-root-toast';
function Input(props) {
const { style, ...otherProps } = props;
return (
<TextInput
{ ...otherProps }
style={[
{
borderWidth: 1,
borderColor: 'lightgray',
padding: 8,
},
styles.input, style
]}
/>
);
}
function TodoView({ todo, toggle }) {
function toggleCompleted() {
Alert.alert(null, `${todo.name}`, [
{
text: 'Cancel',
onPress: () => { },
style: 'cancel',
},
{
text: todo.done ? 'Incomplete' : 'Completed',
onPress: () => toggle(todo.id)
},
]);
}
return (
<TouchableHighlight key={todo} onPress={ toggleCompleted }>
<View
style={{
flex: 1,
flexDirection: 'row',
paddingVertical: 16,
paddingHorizontal: 8,
backgroundColor: 'white', /* requis pour highlight */
borderBottomColor: 'lightgray'
}}
>
<Ionicons name="checkmark-circle" size={22} color={ todo.done ? 'green' : 'lightgray' } />
<View style={{ flex: 1, flexDirection: 'column' }}>
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>{ todo.name }</Text>
{
todo.description &&
<Text style={{ fontSize: 18 }}>{ todo.description }</Text>
}
</View>
</View>
</TouchableHighlight>
);
}
export default function App() {
const EMPTY_TODO = () => { return {
name: null,
done: false,
description: null
}}
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState(EMPTY_TODO());
function add() {
if ((newTodo.name?.trim() ?? '') == '') {
Toast.show('Provide a Todo name', {
duration: Toast.durations.SHORT,
backgroundColor: 'red',
textColor: 'white',
});
} else {
newTodo.id = uuid();
setTodos([newTodo, ...todos]);
setNewTodo(EMPTY_TODO());
}
}
function toggle(id) {
setTodos(
todos.map( t => {
if (t.id == id) {
return {
...t,
done : !t.done
}
}
return t;
})
)
}
function list() {
if (todos.length == 0) {
return (
<View style={{ flex: 1, justifyContent: 'center' }}>
<Text style={{ fontSize: 48, color: 'gray' }}>No todos...</Text>
</View>
)
} else {
return (
<Text>{ JSON.stringify(todos) }</Text>
)
}
}
return (
<>
<StatusBar style="auto" />
<RootSiblingParent>
<SafeAreaProvider>
<SafeAreaView style={styles.container}>
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
<Input
placeholder='New task'
style={{ height: 48, flexGrow: 1 }}
value={ newTodo.name }
onChangeText={ (text) => setNewTodo({...newTodo, name: text}) }
/>
<Switch
value={ newTodo.done }
onValueChange={ checked => setNewTodo({...newTodo, done: checked}) }
/>
</View>
<Input
placeholder='Optional description'
style={{ width: '100%', height: 96, verticalAlign: 'top' }}
multiline={ true }
value={ newTodo.description }
onChangeText={ (text) => setNewTodo({...newTodo, description: text}) }
/>
<Button
title='Add'
color='green'
onPress={ add }
/>
{ list() }
</SafeAreaView>
</SafeAreaProvider>
</RootSiblingParent>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
gap: 16,
alignItems: 'center',
padding: 16,
},
});