Les nouveautés et nouvelles qu'il ne fallait pas manquer sur l'écosystème React / Javascript, d'Avril 2022 🤗
Au programme ce mois-ci :
- On continue à parler un peu de
react@18
- Un proposal de nouvelles méthodes non-destructive pour les
Array
- La sortie de NodeJS version 18
- Gatsby vs Webpack vs NextJS
- Les packages / librairies / outils à surveiller
- Un peu de CSS
- Et on termine par le WTF JS?
React 18
React 18 est sorti le mois dernier. Aujourd'hui on se penche sur le nouveau hook useId
, la dépendance avec react-redux
, et une side effect de useEffect
.
useId
React nous met à disposition un nouveau hook useId()
qui génère un id unique, qui sera persistant lors des re-render de nos composants.
J'ai vu passer plusieurs posts qui disaient se servir de cet hook pour générer des id aléatoires, et venir remplacer un uuid
ou nanoid
dans des .map
. Par exemple :
const App = () => {
const data = [
{ id: useId(), label: 'Item 1' },
{ id: useId(), label: 'Item 2' },
{ id: useId(), label: 'Item 3' },
{ id: useId(), label: 'Item 4' },
];
return (
<ul>
{data.map(d => (
<li key={d.id}>{d.label}</li>
))}
</ul>
)
}
On va faire rapidement : il ne faut PAS utiliser useId
de cette manière. Confirmé par la team ReactJS elle-même : https://twitter.com/reactjs/status/1510334505252491267
(et au passage, il ne faut PAS non plus utiliser uuid
dans les keys
de nos .map
. Vous pouvez en lire plus dans mon précédent article)
Alors à quoi sert ce useId
? Et bien majoritairement pour générer des ID uniques pour des éléments du DOM, notamment pour des besoins d'accessibilité. Par exemple pour coordonner / lier plus facilement les aria-labelledby
, aria-describedby
, aria-owns
, aria-activedescendent
.
Autrement dit, si vous utilisez une librairie UI (par exemple @mui) qui gère tous ces aspects accessibilité, c'est votre librairie qui sera amenée à utiliser useId
de manière interne aux composants, mais il est très peu probable que vous en ayez besoin vous-même !
react-redux
react-redux
se met à jour en version 8 qui apporte une compatibilité complète pour react@18
.
Si comme moi, vous avez sauté sur l'occasion de mettre à jour react@18
, vous avez peut-être remarqué des side-effects avec les thunks, l'asynchrone et les await
qui n'étaient pas toujours pris en compte dans les actions
. C'est désormais réglé !
useEffect
Lors de la migration vers react@18
nous avons eu plusieurs erreurs sur des useEffect
qui faisaient crasher l'application.
Comme vous le savez, vous pouvez retourner une cleanup function dans un useEffect
, qui sera exécutée au unmount ou avant chaque nouveau trigger du useEffect
. Nous avions dans nos composants, à plusieurs reprises, le type de code suivant :
import { dispatch } from 'react-redux';
const App = () => {
const dispatch = useDispatcher();
useEffect(() => dispatch(loadMyDatas()), [dispatch])
return (...)
}
En gros, on dispatch une action au mount du composant, qui va charger nos données, et les stocker dans Redux. Schéma assez banal. Mais écrit comme ci, nous retournons dans notre useEffect
le retour de dispatch(loadMyDatas())
. Ce qui veut dire que la cleanup fonction de notre useEffect
serait donc le retour de dispatch(loadMyDatas())
. Qui n'est PAS une fonction dans ce cas, mais un Object.
Avec react@17
, il semblerait qu'il y avait une "sécurité" qui faisait que si la cleanup fonction n'était pas une fonction, rien ne se passait. Avec react@18
ce cas de figure génère une erreur, et un crash. Il nous a donc fallu modifier tous ces useEffect
pour ne pas retourner de "fausse" cleanup fonction
const App = () => {
const dispatch = useDispatcher();
useEffect(() => {
dispatch(loadMyDatas())
}, [dispatch])
return (...)
}
Nouvelles méthodes Array
Des propositions pour ajouter des méthodes non-destructives pour les tableaux. Tout d'abord quelle est la différence entre des méthodes destructives et non-destructives ?
- Les méthodes destructives vont modifier directement le tableau sur lequel elles sont appliquées. C'est le cas des méthodes
.reverse()
,.sort()
et.splice()
- Les méthodes non-destructives vont retourner un nouveau tableau sans toucher au tableau sur lequel elles sont appliquées. C'est le cas des méthodes
.find()
,.map()
,.forEach()
,.reduce()
,...
Il y a donc 4 nouvelles méthodes au stage proposal :
toReversed()
qui est la version non-destructive de.reverse()
// .reverse()
const arr = ["foo", "bar", "baz"];
arr.reverse();
console.log(arr); // ["baz", "bar", "foo"]
// .toReversed()
const arr = ["foo", "bar", "baz"];
const arr2 = arr.toReversed();
console.log(arr); // ["foo", "bar", "baz"]
console.log(arr2); // ["baz", "bar", "foo"]
toSorted()
qui est la version non-destructive de.sort()
// .sort()
const arr = ["foo", "bar", "baz"];
arr.sort();
console.log(arr); // ["bar", "baz", "foo"]
// .toReversed()
const arr = ["foo", "bar", "baz"];
const arr2 = arr.toSorted();
console.log(arr); // ["foo", "bar", "baz"]
console.log(arr2); // ["bar", "baz", "foo"]
toSpliced()
qui est la version non-destructive de.splice()
// .splice()
const arr = ["foo", "bar", "baz"];
arr.splice(1, 2);
console.log(arr); // ["baz", "foo"]
// .toSpliced()
const arr = ["foo", "bar", "baz"];
const arr2 = arr.toSpliced(1, 2);
console.log(arr); // ["foo", "bar", "baz"]
console.log(arr2); // ["baz", "foo"]
.with()
qui serait la version non-destructive dearr[index] = value
.
// Direct update of index
const arr = ["foo", "bar", "baz"];
arr[1] = "updated";
console.log(arr); // ["foo", "updated", "baz"]
// .with()
const arr = ["foo", "bar", "baz"];
const arr2 = arr.with(1, "updated");
console.log(arr); // ["foo", "bar", "baz"]
console.log(arr2); // ["foo", "updated", "baz"]
NodeJS@18
La version 18 de NodeJS est sortie le 19 Avril, avec notamment le support natif de l'api fetch
! Plus besoin de node-fetch
, unfetch
ou autre librairie tierce !
const res = await fetch('https://nodejs.org/api/documentation.json');
if (res.ok) {
const data = await res.json();
console.log(data);
}
NodeJS ajoute également un module node:test
pour faciliter la création de tests
import test from 'node:test';
test('top level test', async t => {
await t.test('subtest 1', t => {
assert.strictEqual(1, 1);
});
await t.test('subtest 2', t => {
assert.strictEqual(2, 2);
});
});
Gatsby vs Webpack vs NextJS
Gatsby songe à abandonner Webpack pour augmenter ses performances lors du développement et du build. En se basant sur un benchmark plutôt complet, il apparaît en effet que Webpack est largement à la traîne face aux solutions "concurrentes".
Au-delà des gains de performances, se pose évidemment la question de la compatibilité de tous les plugins Gatsby existant, qui reposent pour la grande majorité sur de la configuration Webpack. Et dans ces questionnements, on regarde en général ce que font les autres, et notamment NextJS qui annonce dans sa release 12.1 se baser maintenant sur un Webpack-swc
. Certes moins performant que pourrait être un vitejs
ou esbuild
mais qui semblerait être un bon compromis entre gain de performance, et rétro-compatibilité des plugins.
Si vous voulez en savoir plus dans la réflexion de l'équipe de Gatsby, ca se passe par ici ! Affaire à suivre donc.
Packages / Librairies
- On commence avec GitLanding qui permet de créer un site statique, avec une configuration rapide pour déployer sur github pages. Il supporte par défaut le markdown, et compatible avec @mui et promet un score de 90/100 sur Google Lighthouse. On est donc sur une alternative à Docusaurus
- RedwoodJS qui se qualifie comme le "Rails pour Javascript". Framework complet embarquant React, GraphQL, Prisma, Typescript, Jest et Storybook, et qui compte dans ses développeurs principaux le cofondateur de Github et Jekyll, Tom Preston-Werner. A suivre et voir l'adoption que pourrait avoir ce framework, et la communauté qui se crée derrière.
- Interweave est un composant React qui permet de render de l'html en toute sécurité sans passer par
dangerouslySetInnerHTML
. Il propose également un support natif pour render les emojis, détecter du markup, faire de l'autolinking,... - React-query passe en v4-beta avec le support de React 18
- React-Router et Remix vont fusionner. On devrait donc voir rapidement arriver toutes les évolutions proposées par
Remix
dansreact-router
, ainsi qu'un support pour le<Suspense />
etprefetch
dereact@18
- Lexical est un framework pour créer des RTE (Rich Text Editor). Créé par
FacebookMeta, il sera sûrement le successeur de DraftJS. Très certainement trop tôt pour l'utiliser en production, il reste à surveiller !
Un peu de CSS ?
Zoom sur 2 pseudos-classes :
:focus-within
Vous avez sûrement vu passer plusieurs fois des "Cards" avec du contenu et un CTA qui s'affichent lors du roll-over de la Card, quelque chose dans le genre :
Lorsque vous passez la souris au survol, on a bien tous nos effets de transition. Par contre qu'en est-il lors d'une navigation au clavier avec TAB
pour parcourir la pages ?
Comme nous avons un opacity: 0
ici sur notre bouton, qui ne change qu'au :hover
sur le div parent, et bien nous ne voyons pas le focus de notre bouton. Le problème est assez facile à régler pour le bouton, on va simplement rajouter un
.myButton:focus {
opacity: 1;
}
Nous avons bien le bouton qui apparaît au focus clavier. Mais nous perdons les infos "auteur" et "date" car la visibilité de celles-ci est également défini dans le :hover
du div
parent.
C'est là que la pseudo-classe :focus-within
fait son apparition. Cette pseudo-classe va nous permettre de définir des règles CSS qui s'appliqueront si un élément à l'intérieur de votre élément, a le focus
Avec la navigation clavier sur ce dernier exemple, nous avons bien les données auteur / date qui s'affichent lors du focus de notre button
.myContainer:focus-within .myButton {
opacity: 1
}
.myContainer:focus-within .myMetas {
transform: translateX(0%)
}
On viendra donc très souvent avoir les mêmes règles sur un :hover
et un :focus-within
:focus-within
est bien accepté par tous les navigateurs (sauf IE évidemment, mais on peut laisser mourir ce navigateur non ?)
:has
:has
est une nouvelle pseudo-classe apparue avec Safari 15.4, et qui n'est pour le moment compatible qu'avec Safari
Alors pourquoi s'attarder dessus ? Car c'est une classe qui est "demandée" depuis de nombreuses années par la communauté, et qui pourrait être appelée "selecteur parent".
Vous avez certainement plusieurs fois en CSS voulu avoir la possibilité, à partir du CSS d'un element, de "remonter" à son parent. Par exemple, si mon button
a la classe active
, j'aimerai que son container (= son parent) ait aussi la classe active
pour que je puisse lui affecter des règles CSS particulières. Et bien ce sera possible avec :has
.myContainer:has(button.active) {
background-color: cyan;
}
WTF JS?
Que va afficher ce console.log
?
console.log( parseInt(0.0000005) )
Afficher la solution et les explications !
😱 5
😱
Alors pourquoi ?
Tout d'abord, à partir de la 7ème décimale, JS va stocker les nombres sous leur forme exponentielle. Dans notre cas ici donc 5e-7
Nous avons donc en réalité parseInt(5e-7)
Et vous le sentez venir ? Les fameux transtypages chers à JS ? Et bien vous avez raison ! parseInt
attend un String
et va donc effectuer un String(5e-7)
, qui donne "5e-7"
.
Et parseInt("5e-7")
donne... 5
🤘
Ne manquez pas un article !
Envie de recevoir par email mes derniers articles et Recapt ?
Essayez de taper register <votre adresse email>
dans la console 🎉