Présentation
Cette page correspond à ma prise de notes suite au suivi de formation React présent sur le toujours excellent site GrafiKart.
Réalisée pendant la formation : sans relecture …
Partie 1
Liens vers le CDN : https://fr.reactjs.org/docs/cdn-links.html,
Deux librairies à intégrer : la librairie en tant que tel mais également celle qui gère le DOM,
defer
: permet de demander à ce que le script soit “lu” à la fin du parsing de la page,JSX : pour passer une class css à un élément, il faut utiliser className :
<h1 className="maclasse" >
,JSX :
- Ne fonctionne pas :
<h1 id="title{n}" ><h1>
car tout ce qui entre “” est considéré comme une chaîne de caractère, - Fonctionne :
<h1 id={"title" + n} > </h1>
,
- Ne fonctionne pas :
JSX : pour qu’un JSX soit valide, il lui faut qu’une seule racine (une balise englobante),
React Component :
componentDidMount
: quand un component a été crée / rendu,componentWillUnmount
: quand un component est détruit.
Mise à jour de l’état :
- Il est possible de faire la modification directement,
- Il est cependant conseillé de passer par
setState
surtout quand la mise à jour dépend de l’état, setState
fusionne les champs donc il n’est pas nécessaire de remettre tout l’état,
Le code du render est appelé à chaque tick ou chaque changement d’état,
- Il faut donc faire attention à cela quand on réalise cette fonction,
Premier TP :
- Il faut VRAIMENT penser COMPOSANT : je n’avais pas pensé à faire un composant pour la partie message …,
- Une différence : le température input contenant le scale, je l’ai mis en paramètre de mon appel à la méthode parent,
- Un point que j’oublie en JS : une fonction peut-être une variable.
Partie 2
- Chaque appel d’un
setState
= déclenchement d’un rendu = appel derender
(si classe),
Composant pur :
- un composant qui n’est “rendu” que si une propriété et et un état est modifié,
- extends React.PureComponent si classe ou React.memo si fonction,
- Même si contre intuitif : à conserver pour les composant complexe car nécessite une comparaison de propriétés qui peut avoir un coût équivalent à un rendu,
- En plus cela peut induire également quelques bugs …
- Points de vigilance :
- dans le cas d’un PureComponent, il faut bien modifier la référence de la propriété et pas un “champ” de l’objet. React fait une comparaison simple des objets.,
- Idem pour les fonctions …
Un point détecté pendant mes tests : le changement d’une collection passée en propriété à FilterTableProduct, ne déclenche pas le rendu (en pure ou impur).
Refs et Doms
Il est possible de mapper une propriété du composant avec un élément du DOM. La méthode conseillée :
[...]
constructor(props) {
[...]
this.input = React.createRef();
}
[...]
render() {
[...]
<input type="text" ref={this.input}>
}
Il est alors possible de récupérer l’élement via this.input.current
.
A utilise sur des éléments non mappés ou sur des librairies externes.
Compléments : Pour passer une référence entre composant : React.forwardRef
qui fonctionne facilement avec des fonctions.
Astuce : il est possible en React de passer toute une liste de propriétés à un composants en faisant {…props}. Bon après, il faut que le composant soit prêt à accepter tous les champs …
Partie 3 : Hooks
useState
- Utilisée dans un component “fonction”,
- Permet dans ce genre de component, de créer un “état”,
- /!\ : ne doit pas être utilisée dans une condition ou une fonction,
- React se sert de l’ordre d’exécution pour déterminer le lien entre la fonction et le hook,
- /!\ : il n’y a pas de fusion lors de la mise à jour de l’état. Il faut bien tout reprendre,
- Pour mettre à jour la valeur : passer par un callback peut éviter des sources,
- Exemple :
const [valeur, setter] = useState(valeurInitiale);
useEffect
- Utilisée également dans les components “fonction”,
- Permet de demander l’execution d’une fonction si une valeur passée en paramètre change,
- Il faut bien penser s’il est nécessaire de mettre en place une fonction de nettoyage,
- Permet de simuler les cycles de vie que l’on a dans les classes comme “componentDidMount” :
componentDidMount
: il ne faut pas passer de valeur à observer. Il s’exécutera qu’une seule fois,componentWillUnmount
: il faut utiliser la fonction de retour pour effectuer le nettoyage,componentDidUpdate
: simplement mettre en place la fonction,
- Référence :
useEffect( () => { [ la fonction qui sera appelée ]}, [ tableau de valeur à observer])
- Exemple :
useEffect( () => { document.title = 'Compteur ' + count; }, [ count ]);
NOTE : useEffect
attend une fonction pas une promesse … donc si besoin de fonction qui vont faire des appels asynchrones, il faut créer une fonction dans la fonction qui s’appelle.
useEffect( () => {
(async function() {
})();
}, []);
useMemo
- Permet de mettre en “mémoire” une fonction ou une méthode,
- Particulièrement intéressant pour les fonctions de calcul longue ou pour les handlers de composants purs,
- Exemple :
const handleClick = useMemo(function() { return function() { /* ma fonction */ } });
Il existe un raccourci pour éviter les fonctions de fonctions : useCallBack
.
useRef
Hook qui peut servir d’équivalent à React.createRef
.
Ce hook peut également permettre de persister une valeur entre deux rendus ce qui peut ne pas être le cas dans certaines situation :
const compteur = useRef({count: 0});
const handleButtonClick = function() {
compteur.current.value++;
}
useLayoutEffet
useEffect
est asynchrone. Le rendu du composant pourra être fini avant la fin du traitement. Ce qui peut-être bien pour certains traitement.useLayoutEffect
lui est synchrone. Donc le rendu ne sera pas fait tant qu’il n’a pas fini. A utiliser dans le cas d’un traitement qui manipule le DOM (par exemple).
useReducer
Permet de gérer un état mais de manière plus complexe. useState
est pratique pour gérer des choses “simples” sans trop de logique. Mais dès que de la logique apparaît, useReducer
devient intéressant car il permet de gérer des actions avec de la logique derrière.
function init(initialValue) {
return { count: initialValue };
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count : state.count + (action.payload || 1) };
case 'decrement':
if(state.count <= 0) { return state; }
return { count : state.count - 1 };
case 'init':
return { count : 0 };
default:
throw new Error("L'action " + action.type + " n'est pas disponible");
}
}
[...]
const [count, dispatcher] = useReducer(reducer, 0, init);
[...]
<button onClick={() => dispatcher({type : 'increment', payload: 10}) } > Incrémenter </button>
<button onClick={() => dispatcher({type : 'decrement'}) } > Décrémenter </button>
Bilan
IMPORTANT : Les hooks doivent être appelées toujours dans le même ordre. Pas dans une boucle ou dans un if !
Partie 4 - Notions avancées
Les contextes
- L’objectif des contextes est de pouvoir partager/passer de l’information entre les différents composants sans forcément passer par les propriétés ({props}).
- Créer un contexte (la valeur en paramètre est la valeur par défaut) :
const ThemeContext = React.createContext(theme.dark);
- Mettre en place un provider permettant de définir une valeur :
<ThemeContext.Provider value={theme.light} > <Toolbar /> </ThemeContext.Provider>
- Consommer le contexte :
- Via un composant : (faut passer par une fonction …) :
return <ThemeContext.Consumer> { value => { return <button style={value} > { children }</button> }} </ThemeContext.Consumer>
- Via un hook
useContext
:const value = useContext(ThemeContext);
- Via un composant : (faut passer par une fonction …) :
- Être vigilant sur le contenu de
value
afin d’éviter de trop nombreux rendus, - TP : j’étais pas loin … J’avais mal géré ma fonction change.
Les portals
- Si dans un composant, il nécessaire de placer un élément fils ailleurs dans le DOM :
createPortal
, - Deux exemples : modal ou message d’alerte,
- Ils ne doivent pas forcément être dans le composant mais au niveau de la page principale,
- `ReactDOM.createPortal(
, [l’emplacement]).
Les erreurs
- Deux grands types :
- Erreur dans un callback : l’erreur est affichée mais le rendu se fait,
- Erreur dans un rendu : l’erreur remonte tout le graph et React supprime le dernier élément donc potentiellement le root …,
- Gestion pour le rendu :
- Notion de “Boundary” : éléments qui sert de zone de gestion d’erreurs,
- Pour un composant classe : Il faut implémenter une des deux (ou les deux ) :
componentDidCatch
: Permet de trapper et tracer l’erreur,static getDerivedStateFromError
: Permet de retourner un état suite à la gestion de l’erreur,
- Gestion pour les callbacks : C’est ici
Validation des propriétés d’un composant
- Il faut utiliser un composant externe,
- Ici : prop-types (https://www.npmjs.com/package/prop-types),
- Exemple de déclarations :
// Sur la base d'un composant Double ayant une propriété double Double.propTypes = { n: PropTypes.number.isRequired // Un nombre et obligatoire }
- Ne bloque pas le rendu mais génère une erreur en console (dommage …),
- Différence entre node et element :
- node : tout ce qui peut-être accepté dans une balise HTML (texte, balises, élements …),
- element : element React (donc simple balise aussi).
Test
- Testing librairy React,
- JSDOM : https://github.com/jsdom/jsdom,
Partie 5 - TP
TP : Ajout & Modification :
- Pour lier les champs avec les valeurs, je n’ai pas utilisé
defaultValue
mais je suis passé par desuseRef
, - J’avais une gestion des erreurs “globales”,
- J’avais centralisé le formulaire dans un composant dédié.
- Pour lier les champs avec les valeurs, je n’ai pas utilisé
TP : Gestion des recettes,
Pour injecter de l’HTML :
dangerouslySetInnerHTML
à qui il faut passer un objet :const htmlContent = { __html: recipe.content.split("\n").join("<br/>") }; return <div dangerouslySetInnerHTML={htmlContent} > </div>