Aller au contenu

Syntaxes avancées sur les objets

Comme on l'a vu précédemment, un objet Javascript se comporte en grande partie comme un dictionnaire, et peut être utilisé dans de multiples situations.

Syntaxe abrégée

La syntaxe normale d'un objet littéral est {k1: v1, k2: v2, ...}, cependant il est fréquent d'observer une syntaxe différente :

{a, b, c} équivaut à {a: a, b: b, c: c}, autrement dit :

  • clé "a" -> valeur de la variable a
  • clé "b" -> valeur de la variable b
  • clé "c" -> valeur de la variable c

Les deux syntaxes peuvent être mélangées au sein d'un même objet, par exemple :

const obj = { data, error: error1, isLoading }

qui équivaut à :

const obj = { data: data, error: error1, isLoading: isLoading }

Attention : ne pas confondre cette notation avec l'usage de {} dans JSX !

Affectation par décomposition ("destructuring assignment")

Ce terme désigne la possibilité d'extraire les données d'un objet ou d'une liste dans des variables distinctes.

Les exemples ci-dessous proviennent de la documentation MDN (Mozilla Developer Network) :

let a, b, rest;

// Tableaux
[a, b] = [10, 20];
[a, b, ...rest] = [10, 20, 30, 40, 50];

// Objets
({a, b} = {a: 10, b: 20});
({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});

Dans tous ces exemples, les variables a et b reçoivent 10 et 20 respectivement.

Note : la syntaxe ...rest fait appel à l'"opérateur de décomposition".

Utilisation de cette syntaxe dans les fonctions

En entrée

Une fonction peut prendre en entrée un unique objet (comme props), et le destructurer. Cette syntaxe permet de simuler des paramètres nominatifs, absents en Javascript :

function distance_l2 (props) {
  return math.sqrt(props.x ** 2 + props.y ** 2)
}

Une syntaxe alternative permet de faire apparaître x et y explicitement :

function distance_l2 ({ x, y }) {
  return math.sqrt(x ** 2 + y ** 2)
}

Dans cet exemple particulier, l'intérêt semble limité (pourquoi ne pas utiliser simplement distance_l2(x,y) ?), cependant elle est très utile en React, notamment pour définir et appeler des composants.

Note : Javascript ne permet pas de définir des arguments de fonction nominatifs comme Python. L'utilisation d'objets comme arguments permet de retrouver une fonctionnalité équivalente.

En sortie

Une fonction peut également renvoyer ses résultats sous forme d'un objet. Dans ce cas, on peut utiliser tout ou partie du résultat, en fonction de ses besoins.

Exemple 1

Supposons qu'on ait défini un contexte React qui stocke différentes informations : * utilisateur authentifié * token de l'utilisateur authentifié * langue de l'application * ...

On peut récupérer uniquement les parties qui nous intéressent pour un traitement donné :

import { useContext } from "react";

// L'objet ctx contient des données de contexte, partagées vers tous les composants de l'application
const ctx = useContext(...);
console.log(ctx.user);

// Si on s'intéresse uniquement à une donnée, on peut écrire, par exemple 

const { user } = useContext(...);          // On veut uniquement l'utilisateur en cours
const { token } = useContext(...);         // On veut uniquement le token de l'utilisateur
const { locale } = useContext(...);        // On veut uniquement la locale (langue en cours)
const { user, locale } = useContext(...);  // On veut l'utilisateur et la locale
Supposons qu'on ait défini une fonction setLocale qui permette de charger les messages de texte dans la langue en cours.

Si la fonction est définie comme :

export function setLocale({locale}) {
  ...
} 

alors on peut l'appeler par :

const { locale } = useContext(...);  // On récupère seulement une partie du contexte
setLocale({locale})

voire :

const ctx = useContext(...);  // on récupère tout le contexte...
setLocale(ctx);  //...mais la fonction utilisera seulement la partie qui l'intéresse, { locale } 

Exemple 2 : SWR

import useSWR from 'swr'

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user')

  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>

  return <div>hello {data.name}!</div>
}

Le module SWR définit le hook useSWR, qui appelle une URL et renvoie un objet contenant différents éléments :

  • data : les données reçues
  • error : erreur éventuellement survenue
  • isLoading : un booléen qui indique si les données sont toujours en train de se charger
  • mutate : une fonction qui permet de signaler à SWR de recharger les données
  • ...

En fonction de ses besoins, on peut utiliser seulement une partie des données :

const { data, error } = useSWR('/api/user', fetcher)

ou bien, si on a besoin de pouvoir recharger les données à la demande :

const { data, error, mutate } = useSWR('/api/user', fetcher)

Exemple 3 : react-hook-form

React-hook-form est un module qui simplifie très grandement la gestion des formulaires en React, par le biais d'un hook, useForm.

Version minimale :

const { register, handleSubmit } = useForm();

Version permettant de récupérer en plus les erreurs de validation, et de les afficher :

const { register, handleSubmit, formState: { errors } } = useForm(
    { resolver: yupResolver(editUserSchema) });

Conclusion

L'utilisation de la syntaxe simplifiée et de l'affectation par décomposition fournit une très grande souplesse et expressivité du code. La base de Javascript est extrêmement primitive, mais les évolutions modernes en font un langage beaucoup plus intéressant.

Les composants React étant écrits sous forme de fonctions, on pourra mettre à profit ces syntaxes lors de leur écriture.