C'est parti pour découvrir le state d'un composant React !
Attention, cet article n'est pas à jour.
Mais tout d'abord, voyons une façon de réaliser les deux points laissés en suspens lors de la dernière leçon :
Vous pourrez retrouver les sources de la leçon précédente à l'adresse suivante : github lecon_1
1. Proposition de correction
Rappel de ce que nous cherchons à faire :
- créer un nouveau component "Establishment", en vous inspirant d' App, qui s'occupera de l'affichage d'un établissement dans notre liste.
- utiliser ce même component "Establishment" dans la fonction map vu précédemment.
Voici un exemple de ce à quoi pourrait ressembler le component "Establishment" :
// Fichier : ./src/components/establishment/Establishment.js
import React, { Component } from "react";
class Establishment extends Component {
render() {
return (
<div className="establishment">
<h3>{this.props.establishment.name}</h3>
{this.props.establishment.description}
</div>
);
}
}
export default Establishment;
Rien de bien nouveau ici. Mais j'aimerai vous montrer une autre façon de définir un component :
// Fichier : ./src/components/establishment/Establishment.js [modifié]
import React from "react";
const Establishment = ({ establishment }) => {
return (
<div className="establishment">
<h3>{establishment.name}</h3>
{establishment.description}
</div>
);
};
export default Establishment;
On appelle cela un component stateless, car, comme son nom l'indique, il n'a pas de state. Et c'est pour cela que l'on peut l'écrire sous cette forme "plus légère", qui est aussi (de mon avis) plus facilement compréhensible.
En savoir plus : React Stateless Functional Components: Nine Wins You Might Have Overlooked
Mais... Mais... On ne sait même pas ce que c'est le "state" ...
Cela arrive ! ( mais dans cette leçon le component Establishment ne bougera plus. Du coup libre à vous de l'écrire de cette manière pour le moment. )
Revenons à notre state. Gardons en tête que nous avons besoin de la props establishment qui correspond à l'établissement que l'on affiche. Nous utilisons notre component Establishment de la façon suivante dans le component App :
// Fichier ./src/components/App.js [extrait]
//...
import Establishment from "./establishments/Establishment";
//...
render() {
const listEstablishment = establishments.map(establishment => {
return (
<Establishment
key={establishment.id}
establishment={establishment} // on n'a pas oublié la props "establishment" :)
/>
);
});
//...
}
//...
2. Rappel de l'organisation du projet
src
|___assets
| |
| |___logo.svg
|
|___components
| |
| |___establishments
| | |
| | |___establishments.js
| | |
| | |___fixtures.js
| |
| |___App.js
|
|___css
| |
| |___App.css
| |
| |___index.css
|
|___App.test.js
|
|___index.js
On n'oublie pas de lancer l'application afin de voir les changements dans notre navigateur.
$ cd HappyDrink
$ npm start
3. Utilisation du state d'un component
Bon, j'avoue, je vous ai menti...
Nous n'allons pas, ici, utiliser la méthode getInitialState, car celle-ci est seulement supportée par les classes créées avec React.createClass. Or nous utilisons une "simple" classe JavaScript. Du coup, dans notre cas, nous allons procéder autrement :
// Fichier : ./src/App.js [extrait]
//...
class App extends Component {
constructor(props) {
// Ne pas oublier d'appeler le constructeur père ! (Obligatoire)
super(props);
// On définit le state de notre component que l'on hérite de la classe "Component"
// Cela remplace la fonction "getInitialState"
this.state = {
pseudo: "Inconnu"
};
}
// On définit la fonction appelée lors d'un clic sur le lien "Changer le pseudo !"
// la syntaxe " nomFonction = () => {} " nous permet de conserver le contexte `this` du scope courant. (Ici, la classe App)
// Attention cette syntaxe n'est pas encore conventionnel. C'est Create-React-App aui nous permet de le faire grâce
// à une config babel particulière
randomPseudo = () => {
// On s'amuse un peu ;)
let randomPseudo = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const size = Math.floor(Math.random() * 10) + 5;
for (let i = 0; i < size; i++)
randomPseudo += possible.charAt(
Math.floor(Math.random() * possible.length)
);
// On met à jour le state via la fonction "setState" héritée de la classe Component
this.setState({
pseudo: randomPseudo
});
};
render() {
//...
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>
Welcome "{this.state.pseudo}" to {this.props.title}
</h2>
</div>
<div className="App-intro">
// On appelle notre fonction lors du clic sur le lien
<p>
<a onClick={this.randomPseudo}>Changer le pseudo !</a>{" "}
</p>
<section>{listEstablishment}</section>
</div>
</div>
);
}
//...
}
export default App;
Et voilà !
Qu'avons-nous vu d'important à retenir ?
Initialisation du state de notre component
On initialise l'objet state avec :
- pour clé : les différents noms de variables que vous voulez garder dans le state
- et en valeur : la valeur de ces variables.
Modifier le state de notre component
On fait appel à la fonction setState, et on lui passe en argument un nouvel objet ayant :
- pour clé : le nom des variables du state que l'on veut modifier
- et en valeur : la nouvelle valeur de la variable.
Exemple Rapide :
state = {
pseudo: "test",
age: 18
};
//Ici je ne passe en paramètre que ce que je souhaite modifier
this.setState({
pseudo: "randomPseudo"
});
Bonus
En utilisant la fonction setState , React détecte qu'il y a eu un changement. Et comme nous l'avons vu précédemment, lors de cette situation, React rappelle la méthode render des components.
C'est cela qui nous permet de voir le changement du pseudo lors de chaque clic.
Disclaimer
Quand vous construisez un component, posez-vous ces questions :
- Ma variable m'est passée par mon parent via les props ? Ne la mettez pas dans le state
- Ma variable n'est pas amenée à changer dans le temps ? Ne la mettez pas dans le state
- Ma variable peut être déterminer à partir d'autres variables du state ou des props ? Ne la mettez pas dans le state
- Ma variable ne remplie aucun des cas ci-dessus ? Alors sa place est surement dans le state
En savoir plus : Thinking in React
4. Récapitulatif
Regardons un peu l'avancement de notre projet HappyDrink :
- lister les bars.
- filtrer la liste.
- mettre en favori un bar.
- visualiser l'happy-hour de celui-ci.
- liker/disliker ce bar.
Bonus :
- Afficher un pseudo random.
Mais... On n'a pas avancé là ! Remboursez !
Ne vous inquiétez pas, cela arrive !
Vous pourrez retrouver les sources de cette leçon à l'adresse suivante : github lecon_2
5. Pour la suite
Maintenant que nous avons vu les deux principaux fondements de React, nous sommes capables de construire entièrement notre application ! Mais allons-y pas à pas, tout en découvrant au fur et à mesure différents autres aspects de React.
De plus on n'a pas encore parlé de Redux !
Je vous propose donc, pour l'instant, de réaliser les tâches suivantes :
- créer un bouton like et un bouton dislike pour chacun des établissements
- incrémenter un compteur de like et de dislike lors d'un clic sur le bouton correspondant
- afficher le compteur associé à chacun des boutons
- créer un bouton favori (une icône étoile par exemple) qui a un fond transparent s'il n'est pas en favori et jaune s'il l'est
Nous corrigerons cela dans la prochaine leçon !