Académique Documents
Professionnel Documents
Culture Documents
1
Parcours Cours Alternance Employeurs
Accueil > Cours > Développez une application mobile React Native > Manipulez le State
( )
Maîtrisez les
Manipulez le State + bases de React ,
Native
2. Appliquez des
styles à vos
components
3. Construisez vos
vues avec Flexbox
5. Manipulez le State
6. Ajoutez des
fonctionnalités sur
00:31
un component
7. Appréhendez le
"setState"
Dans ce chapitre, nous allons découvrir et utiliser l'API TMDB (The Movie DataBase) Native
pour récupérer toutes sortes de films. Vous verrez que l'API TMDB est très
puissante et surtout qu'elle permet de récupérer les informations des films en
/ 0 1
français ! Tout le contenu et la richesse de cette API sont créés par une
/search - Text based search is the most common way. You provide a query
string and we provide the closest match. Searching by text takes into account
all original, translated, alternative names and titles.
La recherche fonctionne sur le titre du film et quelle que soit la langue. C'est
parfait.
10:07
On va commencer par une étape un peu fastidieuse qui est la récupération d'un
token sur l'API, car oui, même si l'API est gratuite, il faut un token pour l'utiliser.
Celui-ci se récupère en créant un compte et un projet sur l'API.
Mettez bien une adresse mail valide et à laquelle vous avez accès, car il faut
activer votre compte avec un lien dans le mail reçu.
Vous allez recevoir un mail "Email Verification Required" pour activer votre compte.
Cliquez sur le lien du mail. Vous êtes redirigé vers l'écran de login, connectez-vous
avec les identifiants utilisés précédemment pour la création de votre compte.
Tout en haut à droite, vous devriez voir une pastille avec la première lettre de votre
nom d'utilisateur (moi, c'est le M) :
Assez logiquement, ici, nous allons cliquer sur le lien dans "Demander une clé API".
Il faut mettre un peu de texte dans "Résumé de l'application", sinon cela ne passe
pas.
C'est fini, vous arrivez sur un écran où vous retrouvez, tout en bas, le token dont on
a besoin :
Prenez bien le token dans "Clé de l'API (v3 auth)". Ne faites pas l'erreur de
prendre "Jeton d'accès en lecture à l'API (v4 auth)".
Bravo, vous avez terminé l'étape la plus fastidieuse de ce cours et vous êtes fin
prêt à utiliser toute la puissance de l'API TMDB. Sans plus attendre, je vous propose
de mettre en pratique ce token et de récupérer des films en fonction de la
recherche effectuée.
,
C'est une très bonne pratique de séparer la logique API du reste de l'application.
Pour cela, on va créer un nouveau dossier et un nouveau fichier avec tous nos
appels API. Créer un dossier API à la racine de votre projet et, à l'intérieur de celui-
ci, un fichier TMDBApi.js.
1 // API/TMDBApi.js
2
3 const API_TOKEN = "VOTRE_TOKEN_ICI";
Ensuite, on crée une méthode pour rechercher des films en fonction d'un texte et
on constitue l'URL à appeler avec le texte fourni et notre API_TOKEN . Pour savoir
comment construire l'URL, il faut se référer à la documentation de l'API TMDB :
javascript
1 // API/TMDBApi.js
2
3 const API_TOKEN = "VOTRE_TOKEN_ICI";
4
5 export function getFilmsFromApiWithSearchedText (text) {
6 const url = 'https://api.themoviedb.org/3/search/movie?api_key=' +
API_TOKEN + '&language=fr&query=' + text
7
8 }
J'ai défini cette fonction avec export pour que l'on puisse l'utiliser dans
nos components tout à l'heure.
Il ne nous reste plus qu'à faire l'appel à l'API. React Native vous propose d'utiliser
l'API Fetch dans sa documentation pour tous vos appels réseaux. Fetch est très
simple à utiliser. On va partir sur cela, mais rien ne vous empêche plus tard
d'utiliser d'autres services pour vos appels réseaux : XMLHttpRequest ou
encore Axios.
javascript
1 // API/TMDBApi.js
2
3 const API_TOKEN = "VOTRE_TOKEN_ICI";
4
5 export function getFilmsFromApiWithSearchedText (text) {
6 const url = 'https://api.themoviedb.org/3/search/movie?api_key=' +
API_TOKEN + '&language=fr&query=' + text
7 return fetch(url)
8 .then((response) => response.json())
9 .catch((error) => console.error(error))
10 }
Dans notre component custom Search, on va appeler cette méthode au clic sur le
bouton "Rechercher". Pour cela, on crée une action sur notre bouton :
jsx
1 // Components/Search.js
2
3 class Search extends React.Component {
4
5 _loadFilms() {
6
7 }
8
9 render() {
10 return (
11 <View style={styles.main_container}>
12 <TextInput style={styles.textinput} placeholder='Titre du film'/>
13 <Button title='Rechercher' onPress={() => this._loadFilms()}/>
14 <FlatList
15 data={films}
16 keyExtractor={(item) => item.id.toString()}
17 renderItem={({item}) => <FilmItem film={item}/>}
18 />
19 </View>
20 )
21 }
22 }
Je précise bien "indiquer", car cela ne rend pas votre méthode privée, vous
pourrez toujours l'appeler n'importe où dans vos fichiers Javascript.
Simplement, c'est un bon moyen de se rendre compte que l'on accède à
une méthode (ou propriété) à laquelle on ne devrait pas accéder.
Cette indication est très utilisée en React, vous aurez l'occasion de la voir
dans beaucoup d'exemples sur le web.
1 // Components/Search.js
2
3 import { getFilmsFromApiWithSearchedText } from '../API/TMDBApi' // import
{ } from ... car c'est un export nommé dans TMDBApi.js
1 // Components/Search.js
2
3 _loadFilms() {
4 getFilmsFromApiWithSearchedText("star").then(data =>
console.log(data));
5 }
C'est tout bon. Placez-vous sur votre application, mettez votre terminal en
évidence. Cliquez sur le bouton "Rechercher", vous devriez voir apparaître plein de
logs avec des données de films. Je ne mets pas tout, mais cela ressemble à ça :
13:57:54: Object {
13:57:54: "page": 1,
13:57:54: "results": Array [
13:57:54: Object {
13:57:54: "adult": false,
13:57:54: "backdrop_path": "/c4zJK1mowcps3wvdrm31knxhur2.jpg",
13:57:54: "genre_ids": Array [
13:57:54: 12,
13:57:54: 28,
13:57:54: 878,
13:57:54: ],
13:57:54: "id": 11,
13:57:54: "original_language": "en",
13:57:54: "original_title": "Star Wars",
...
Bravo ! Vous venez de réaliser votre tout premier appel sur l'API TMDB.
Bon, afficher des films dans un terminal, c'est bien. Les afficher dans une
application, c'est mieux.
Pour afficher les films de l'API, il faut déjà les stocker dans une propriété de notre
component. En React, on a pour habitude de définir nos propriétés dans le
constructeur du component. Vous n'êtes pas obligé de faire pareil, mais c'est une
bonne pratique.
1 // Components/Search.js
2
3 class Search extends React.Component {
4
5 constructor(props) {
6 super(props)
7 // Ici on va créer les propriétés de notre component custom Search
8 }
9
10 //...
11 }
Je vous invite à créer une propriété _films et à l'initialiser avec un tableau vide
pour l'instant. Vous pouvez également supprimer l'import
import films from '../Helpers/filmsData' , il ne nous servira plus :
javascript
1 // Components/Search.js
2
3 class Search extends React.Component {
4
5 constructor(props) {
6 super(props)
7 this._films = []
8 }
9
10 //...
11 }
On va dire à notre liste de films (FlatList) d'utiliser cette propriété pour gérer ses
données :
jsx
1 // Components/Search.js
2
3 <FlatList
4 data={this._films}
5 keyExtractor={(item) => item.id.toString()}
6 renderItem={({item}) => <FilmItem film={item}/>}
7 />
Il ne nous reste plus qu'à modifier notre propriété _films avec les films
récupérés depuis l'API TMDB et à mettre à jour notre liste de données. Si
vous faites attention aux logs que l'on a affichés tout à l'heure lors de l'appel API,
vous avez pu voir que les films sont stockés dans un tableau results . On va
donc récupérer nos films dans ce tableau :
javascript
1 // Components/Search.js
2
3 _loadFilms() {
4 getFilmsFromApiWithSearchedText("star").then(data => {
5 this._films = data.results
6 this.forceUpdate()
7 })
8 }
Basculez sur votre application, un reload sera sûrement nécessaire. Cliquez sur le
bouton "Rechercher" :
Yes ! Ce n'était pas si compliqué, n'est-ce pas ? Allez-y, scrollez dans la liste des
films ! Vous allez retrouver vos films récupérés depuis l'API.
Bon, je suis désolé, je casse un peu l'ambiance, mais en fait, on a fait un truc pas
bien du tout ici. On a forcé un component à se re-rendre avec la méthode
forceUpdate() . C'est une chose à éviter.
C'est vrai, mais je vais vous montrer une solution beaucoup plus propre et efficace
pour re-rendre un component. C'est une solution qui existe depuis longtemps
dans le développement web : le State.
Le State
,
Pour vous présenter le state, je vais m'appuyer sur la définition faite par React
Native, elle est très parlante.
Les props (que l'on a déjà vues) sont fixées par le component parent et ne
peuvent pas être modifiées par le component qui les reçoit. Par exemple, pour
nos items FilmItem, on a défini, depuis le component parent Search, une prop
film :
jsx
Attention, toutefois, à ne pas faire de raccourci trop vite. Ne pensez pas que
les props d'un component ne peuvent pas changer. Elles le peuvent ;
simplement, c'est le component parent qui va les changer.
Si, dans notre exemple précédent, vous souhaitez changer le film affiché
dans un component FilmItem, c'est bien le component parent, le
component Search, qui va lui fournir les nouvelles props.
Dès lors que vous souhaitez modifier votre component et ses données
affichées, vous allez utiliser son state.
Dans notre component Search, on gère un tableau de films que l'on affiche
ensuite dans une FlatList. Ce tableau de films est modifié à chaque fois que l'on
appelle l'API TMDB. C'est l'exemple parfait pour utiliser le state.
Initialisez le state
Comme pour les variables d'un component, on a pour habitude d'initialiser le state
dans le constructeur du component. On va donc initialiser notre state avec un
tableau de films vide :
javascript
1 // Components/Search.js
2
3 constructor(props) {
4 super(props)
5 this.state = { films: [] }
6 }
Le nom de cette propriété est fixe, vous ne pouvez pas le changer. Si vous
faites this.myState = ... , par exemple, vous créez juste une variable
qui n'a rien à voir avec le state.
Ensuite, on va dire à notre FlatList d'utiliser cette nouvelle donnée pour afficher ses
films :
jsx
1 // Components/Search.js
2
3 <FlatList
4 data={this.state.films}
5 keyExtractor={(item) => item.id.toString()}
6 renderItem={({item}) => <FilmItem film={item}/>}
7 />
Il ne nous reste plus qu'à modifier le state avec les films récupérés depuis l'API.
Modifiez le state
Pour modifier la valeur films de votre state, je suis sûr que vous avez pensé à
En React, pour modifier une donnée du state, on passe toujours par setState
.
1 // Components/Search.js
2
3 _loadFilms() {
4 getFilmsFromApiWithSearchedText("star").then(data => {
5 this.setState({ films: data.results })
6 })
7 }
"Rechercher" et TADAAM :
Nous avons modifié les données de notre component Search, le state, en passant
par la fonction setState . React a identifié que le state de votre component
Search a changé. Il va alors demander à votre component Search de se re-rendre
avec le nouveau state. Tout votre component Search est re-rendu et, cette fois,
this.state.films contient la liste des films de l'API. Votre FlatList utilise ces
nouvelles données et les affiche.
Vous ne me croyez pas quand je vous dis que le component est entièrement re-
rendu ? Vous avez raison de ne pas me croire sur parole. Je vous propose une
petite démonstration. Ajoutez un log dans la méthode render de votre
component :
jsx
1 // Components/Search.js
2
3 render() {
4 console.log("RENDER")
5 return (
6 ...
7 )
8 }
09:06:02: RENDER
C'est normal, au premier affichage du component, celui-ci est rendu. Au clic sur le
bouton "Rechercher", on peut voir :
09:06:02: RENDER
09:06:05: RENDER
Je ne vous ai pas menti. Une fois setState appelé, TOUT le component est re-
rendu.
React préconise fortement de n'utiliser que les données provenant des props et
du state dans le render de vos component. Ainsi, vous vous assurez que vos
components sont toujours mis à jour dès que leurs informations changent.
C'est une excellente pratique que l'on a toujours utilisée dans ce cours, sans
vraiment s'en rendre compte. Si vous regardez le render de notre
component FilmItem, vous verrez que l'on n'utilise que des données
provenant des props.
,
Une erreur que l'on commet régulièrement, moi le premier, est d'utiliser le state à
tout va pour gérer toutes les données d'un component. Vous allez voir, dans la
prochaine fonctionnalité que l'on va mettre en place, que, parfois, utiliser le state
pour gérer une donnée de notre component est une mauvaise idée.
Pour l'instant, le seul évènement qui survient sur notre TextInput, c'est
l'évènement onChangeText , appelé à chaque fois que l'utilisateur saisit un
caractère. Si vous me suivez toujours, cela signifie que, pour être sûr d'avoir la
bonne valeur du TextInput, vous devez la récupérer à chaque fois que l'utilisateur
saisit un caractère.
Si vous ne récupérez pas la valeur pendant la saisie et que l'utilisateur clique sur le
bouton "Rechercher", c'est foutu ! L'évènement appelé par le clic est l'évènement
onPress du Button. Aucun évènement n'a lieu sur le TextInput à cet instant,
vous ne pourrez pas récupérer le texte du TextInput.
Nous allons donc récupérer le texte saisi à chaque caractère saisi et, pour vous
montrer pourquoi il ne faut pas stocker n'importe quelle donnée dans le state, on
va stocker le texte dans le state :
jsx
1 // Components/Search.js
2
3 class Search extends React.Component {
4 constructor(props) {
5 super(props)
6 this.state = {
7 films: [],
8 searchedText: "" // Initialisation de notre donnée searchedText
dans le state
9 }
10 }
11
12 _searchTextInputChanged(text) {
13 this.setState({ searchedText: text })
14 }
15
16 render() {
17 console.log("RENDER")
18 return (
19 <View style={styles.main_container}>
20 <TextInput
21 style={styles.textinput}
22 placeholder='Titre du film'
23 onChangeText={(text) => this._searchTextInputChanged(text)}
24 />
25 //...
26 </View>
27 )
28 }
29 }
Ici, j'ai défini une propriété searchedText dans mon state et, à chaque
modification du texte du TextInput, j'appelle setState pour mettre à jour ma
propriété searchedText de mon state.
1 // Components/Search.js
2
3 _loadFilms() {
4 console.log(this.state.searchedText) // Un log pour vérifier qu'on a
bien le texte du TextInput
5 if (this.state.searchedText.length > 0) { // Seulement si le texte
recherché n'est pas vide
6 getFilmsFromApiWithSearchedText(this.state.searchedText).then(data =>
{
19:05:01 Harry
Vous avez raison, cela marche, mais je vais vous montrer le problème ici. Ajoutez
un log tout simple, comme tout à l'heure, dans le render de notre component
Search :
jsx
1 // Components/Search.js
2
3 render() {
4 console.log("RENDER")
5 return (
6 ...
7 )
8 }
Lancez votre application, vous devriez avoir un premier log montrant que l'on
appelle bien le render de notre component à son initialisation :
19:10:10 RENDER
19:10:10 RENDER
19:10:10 RENDER
19:10:10 RENDER
19:10:10 RENDER
19:10:10 RENDER
On vient de re-rendre notre component Search 5 fois, une fois pour chaque lettre
saisie. Et le pire dans tout cela est qu'on l'a re-rendu pour rien. Notre component
Search a les mêmes informations affichées qu'avant de le re-rendre 5 fois. On n'a
pas ajouté, modifié ou supprimé de films et le rendu reste le même.
Cette observation me permet de vous introduire une très bonne pratique. Dans le
state, on ne gère que des données qui, une fois modifiées, peuvent affecter le
rendu de notre component.
Nos films modifient l'affichage de notre component, ils ont donc leur place
dans le state.
Le texte du TextInput ne modifie pas l'affichage de notre component, il n'a
pas sa place dans le state.
comme ici avec les films, c'est qu'elle a sa place dans le state. Autrement, sa
place dans le state peut être remise en question.
C'est tout simplement parce que ce n'est pas grave en soit et que le re-
rendement des components est très bien géré et optimisé par React.
Simplement, je souhaite éviter avec vous une pratique que j'ai pu retrouver
sur certaines applications et qui consiste à tout mettre dans le state, sans
se poser de questions.
Ici, on va plutôt gérer le texte de notre TextInput comme une simple variable de
notre component :
jsx
1 // Components/Search.js
2
3 import React from 'react'
4 import { StyleSheet, View, TextInput, Button, Text, FlatList } from 'react-
native'
5 import FilmItem from './FilmItem'
6 import { getFilmsFromApiWithSearchedText } from '../API/TMDBApi'
7
8 class Search extends React.Component {
9
10 constructor(props) {
11 super(props)
12 this.searchedText = "" // Initialisation de notre donnée searchedText
en dehors du state
13 this.state = {
14 films: []
15 }
16 }
17
18 _loadFilms() {
19 if (this.searchedText.length > 0) { // Seulement si le texte recherché
n'est pas vide
20 getFilmsFromApiWithSearchedText(this.searchedText).then(data => {
21 this.setState({ films: data.results })
22 })
23 }
24 }
25
26 _searchTextInputChanged(text) {
27 this.searchedText = text // Modification du texte recherché à chaque
saisie de texte, sans passer par le setState comme avant
28 }
29
30 render() {
31 console.log("RENDER")
32 return (
33 <View style={styles.main_container}>
34 <TextInput
35 style={styles.textinput}
36 placeholder='Titre du film'
37 onChangeText={(text) => this._searchTextInputChanged(text)}
38 />
39 <Button title='Rechercher' onPress={() => this._loadFilms()}/>
40 <FlatList
41 data={this.state.films}
42 keyExtractor={(item) => item.id.toString()}
43 renderItem={({item}) => <FilmItem film={item}/>}
44 />
45 </View>
46 )
47 }
48 }
49
50 const styles = StyleSheet.create({
51 main_container: {
52 flex: 1,
53 marginTop: 20
54 },
55 textinput: {
56 marginLeft: 5,
57 marginRight: 5,
58 height: 50,
59 borderColor: '#000000',
60 borderWidth: 1,
61 paddingLeft: 5
62 }
63 })
64
65 export default Search
Vous pouvez tester et constater que notre application fonctionne tout aussi
bien sans utiliser le texte du TextInput dans le state. Côté logs, on n'a plus que deux
appels au render du component Search :
19:14:20 RENDER
19:14:25 RENDER
Mais surtout, vous savez manipuler et utiliser le state, sans trop en abuser, hein ?
C'est un concept très important en React. Le state et les props gèrent les
données de vos components.
Le super setState vous assure de re-rendre vos components dès que c'est
nécessaire. Fini les forceUpdate() . En utilisant setState , on laisse
React utiliser toute sa puissance pour gérer nos components et, pour l'instant, cela
marche plutôt bien.
AJOUTEZ DES
+ UTILISEZ LES PROPS FONCTIONNALITÉS SUR UN ,
COMPONENT
Vidéo
Blog d'Utilisation
Nous contacter