Maison > interface Web > js tutoriel > le corps du texte

La prochaine amélioration de la réactivité angulaire

Mary-Kate Olsen
Libérer: 2024-11-17 22:19:01
original
191 Les gens l'ont consulté

The next improvement in Angular reactivity

Introduction

Depuis les dernières versions d'Angular, un nouveau système de réactivité primitive a été développé dans le cadre : les signaux !

Aujourd'hui, avec le recul, nous nous rendons compte que certains cas d'usage n'avaient pas été abordés, et évidemment l'équipe Angular étant très réactive nous fournira des aides pour couvrir ces cas d'usage.

Quels sont ces cas d’utilisation ? Quelles solutions vont être mises en place, et comment vont-elles être utilisées ?

Processus de réinitialisation d'un signal par rapport à un autre

Commençons par illustrer ce problème.

Imaginons que nous ayons une corbeille de fruits avec une certaine quantité.
La quantité est gérée par un composant qui introduit le fruit.

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Ici, la variable doit être réinitialisée si le prix d'entrée du fruit change.

Une solution simple serait d'utiliser un effet

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = signal(1);

  countEffect(() => {
    this.fruit();
    this.quantity.set(1);
  }, { allowSignalWrites: true })

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion

Le code précédent est une mauvaise pratique. Pourquoi est-ce la grande question ?

Nous devons définir l'option signalWrites sur true afin de définir la quantité de signal. Cela est dû à une mauvaise interprétation du problème posé.
Dans notre cas, nous souhaitons synchroniser deux variables qui, dans notre matérialisation, sont désynchronisées

Le comptoir n'est pas indépendant du fruit, qui est notre source initiale. En réalité, nous avons ici un état composant, dont la source initiale est le fruit et le reste est un dérivé du fruit.

Matérialisez le problème comme suit

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{fruitState().quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();

  fruitState = computed(() => ({
    source: fruit(),
    quantity: signal(1),
  }));

  updateQuantity(): void {
    this.fruitState().quantity.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion

Cette matérialisation lie fortement le fruit à sa quantité.
Ainsi dès que le fruit change, la variable calculée fruitState est automatiquement recalculée. Ce recalcul renvoie un objet avec la propriété quantité, qui est un signal initialisé à 1.

En renvoyant un signal, la variable peut être incrémentée au clic et simplement réinitialisée lorsque le fruit change.

C'est un patron relativement simple à mettre en place, mais ne peut-on pas le simplifier ?

La fonction LinkedSignal à la rescousse.

Avec l'arrivée d'Angular 19, une nouvelle fonction de calcul de signaux dérivés.

Jusqu'à présent, nous avions la fonction calculée, mais cette fonction renvoie un Signal et non un WrittableSignal, ce qui aurait été pratique dans notre précédent cas d'utilisation de la variable quantité.

C'est là qu'intervient LinkedSignal. LinkedSignal, comme son nom l'indique, vous permet de lier fortement deux signaux entre eux.

Si l'on revient à notre cas précédent, cette fonction nous permettrait de simplifier le code ainsi :

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = linkedSignal({ source: fruit, computation: () => 1 });

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion

La fonction linkedSignal est définie comme suit :

linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;
Copier après la connexion
Copier après la connexion

Dans la première définition, la définition « abrégée », la fonction linkedSignal prend une fonction de calcul comme paramètre et un objet de configuration.

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Dans cet exemple précédent, comme la fonction de calcul dépend du signal de quantité, lorsque la quantité change, la fonction de calcul est réévaluée.

Dans la deuxième définition, la méthode linkedFunction prend en paramètre un objet avec trois propriétés

  • la source : le signal sur lequel la fonction de calcul base sa réévaluation
  • la fonction de calcul
  • un objet paramètre

Contrairement à la fonction de calcul « abrégée », ici la fonction de calcul prend en paramètres la valeur de la source et un « précédent ».

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = signal(1);

  countEffect(() => {
    this.fruit();
    this.quantity.set(1);
  }, { allowSignalWrites: true })

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion

La nouvelle API de ressources

Angular 19 introduira une nouvelle API pour une récupération simple des données et la récupération de l'état des requêtes (en attente, etc.), des données et des erreurs.

Pour ceux qui connaissent un peu le framework, cette nouvelle API fonctionne un peu comme le hook useRessource.

Regardons un exemple :

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{fruitState().quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();

  fruitState = computed(() => ({
    source: fruit(),
    quantity: signal(1),
  }));

  updateQuantity(): void {
    this.fruitState().quantity.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion

Il y a plusieurs choses à savoir sur cet extrait de code

Il y a plusieurs choses à noter dans cet extrait de code :

  • par défaut, le chargeur prend une promesse en paramètre
  • le type fruitRessource est un WrittableRessource, ce qui nous permettra de modifier les données localement si nécessaire
  • la demande de détails sur les fruits est envoyée directement au serveur lors de la création de la ressource fuitResource
  • this.fruitId() n'est pas suivi dans le chargeur, donc aucune nouvelle requête ne sera envoyée si le fruitId change
  • WrittableRessource a une méthode d'actualisation pour actualiser les données

L'effet suivant imprimera ces valeurs

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  quantity = linkedSignal({ source: fruit, computation: () => 1 });

  updateQuantity(): void {
    this.quantity.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion

comme expliqué ci-dessus, par défaut, le signal fruitId n'est pas suivi.

Alors comment redémarrer la requête http à chaque fois que la valeur de ce signal change, mais aussi comment annuler la requête précédente dans le cas où la valeur du signal fruitId change et que la réponse à la requête précédente ne change pas arriver ?

La fonction ressource prend une autre propriété appelée request.

Cette propriété prend comme valeur une fonction qui dépend des signaux et renvoie leur valeur.

linkedSignal(computation: () => D, options?: { equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;

linkedSignal(options: { source: () => S; computation: (source: NoInfer<S>, previous?: { source: NoInfer<S>; value: NoInfer<D>; }) => D; equal?: ValueEqualityFn<NoInfer<D>>; }): WritableSignal<D>;
Copier après la connexion
Copier après la connexion

Comme indiqué dans le code ci-dessus, la fonction de chargement prend deux paramètres

  • la valeur de la requête : ici la valeur du signal fruitId
  • la valeur de la propriété signal de l'objet abortController utilisée pour annuler une requête http.

Donc si la valeur du signal fruitId change lors d'une httpRequest récupérant les détails d'un fruit, la requête sera annulée pour lancer une nouvelle requête.

Enfin, Angular a également pensé à la possibilité de coupler cette nouvelle api avec RxJs, nous permettant ainsi de bénéficier de la puissance des opérateurs Rx.

L'interporabilité est obtenue à l'aide de la fonction rxResource, qui est définie exactement de la même manière que la fonction ressource.
La seule différence sera le type de retour de la propriété loader, qui renverra un observable

@Component({
  template: `<button type="button" (click)="updateQuantity()"> 
    {{quantity()}}
    </button>`
})
export class QuantityComponent() {
  fruit = input.required<string>();
  count = signal(1);

  updateQuantity(): void {
    this.count.update(prevCount => prevCount++);
  }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Ici il n'est pas nécessaire d'avoir le abortSignal, l'annulation de la requête précédente lorsque la valeur du signal fruitId change est implicite dans la fonction rxResource et le comportement sera le même que celui de l'opérateur switchMap.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal