Replay

Apprendre Angular en 1h

Je me donne un objectif: vous faire découvrir et apprendre Angular en 1 heure: composant, syntaxe dans les templates, les directives, les signaux, les routeurs, les services, l'injection de dépendances, les observables et les requêtes HTTP. Le nécessaire pour faire une application Angular !.

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

Cycle de vie dans NgRx Signal Store

Le cycle de vie dans NgRx Signal Store correspond aux différentes phases qu'un store traverse depuis sa création jusqu'à sa destruction. Comprendre et utiliser ces cycles de vie vous permet d'initialiser correctement votre état, de réagir aux changements, et de nettoyer les ressources lorsqu'elles ne sont plus nécessaires.

Les cycles de vie dans la vie quotidienne

Pour illustrer ce concept, pensez à l'ouverture et à la fermeture d'un magasin chaque jour. À l'ouverture (initialisation), les employés préparent le magasin, vérifient l'inventaire et mettent en place les présentoirs. Pendant la journée (phase active), ils servent les clients et réapprovisionnent les rayons. À la fermeture (destruction), ils ferment la caisse, nettoient le magasin et sécurisent les locaux. Chaque phase a ses propres responsabilités et processus spécifiques.

withHooks : Ajouter des hooks de cycle de vie

NgRx Signal Store propose l'utilisation de withHooks pour définir des fonctions qui seront exécutées à des moments spécifiques du cycle de vie d'un store :

ts
import { signalStore, withState, withHooks } from '@ngrx/signals';
import { inject } from '@angular/core';
import { UserService } from './user.service';

interface UserState {
  users: User[];
  selectedUserId: number | null;
  isLoading: boolean;
}

export const UserStore = signalStore(
  { providedIn: 'root' },
  
  withState<UserState>({
    users: [],
    selectedUserId: null,
    isLoading: false
  }),
  
  withHooks({
    // Hook exécuté lors de l'initialisation du store
    onInit(store) {
      console.log('UserStore initialized');
      
      // Exemple : charger les utilisateurs au démarrage
      const userService = inject(UserService);
      store.loadUsers();
    },
    
    // Hook exécuté lorsque le store est détruit
    onDestroy() {
      console.log('UserStore destroyed');
      // Nettoyage des ressources si nécessaire
    }
  })
);

DISPONIBILITÉ

Notez que si votre store est injecté au niveau racine avec providedIn: 'root', le hook onDestroy ne sera jamais appelé car le store existe pendant toute la durée de vie de l'application.

Hooks disponibles

onInit

Le hook onInit est exécuté lorsque le store est initialisé, c'est-à-dire lorsqu'il est injecté pour la première fois. C'est l'endroit idéal pour :

  • Charger des données initiales
  • Configurer des écouteurs d'événements
  • Initialiser des services externes
ts
withHooks({
  onInit(store) {
    // Injecter des services
    const http = inject(HttpClient);
    const router = inject(Router);
    
    // Charger des données initiales
    http.get<User[]>('/api/users').subscribe(users => {
      patchState(store, { users, isLoading: false });
    });
    
    // Configurer des écouteurs d'événements
    const subscription = someObservable.subscribe(/* ... */);
    
    // Retourner une fonction de nettoyage (facultatif)
    return () => subscription.unsubscribe();
  }
})

onDestroy

Le hook onDestroy est appelé lorsque le store est détruit. Il est utile pour :

  • Annuler des abonnements
  • Sauvegarder l'état
  • Libérer des ressources
ts
withHooks({
  onDestroy(store) {
    // Sauvegarder l'état dans localStorage
    localStorage.setItem('userState', JSON.stringify({
      selectedUserId: store.selectedUserId()
    }));
    
    // Nettoyer les ressources
    console.log('UserStore cleaned up');
  }
})

Fonction de nettoyage depuis onInit

Le hook onInit peut également retourner une fonction de nettoyage qui sera appelée lorsque le store est détruit, similaire à un effect cleanup dans React :

ts
withHooks({
  onInit(store) {
    // Configuration
    const subscription = someObservable.subscribe(/* ... */);
    
    // Retourner une fonction de nettoyage
    return () => {
      console.log('Cleaning up from onInit');
      subscription.unsubscribe();
    };
  }
})

Hooks personnalisés

Vous pouvez également créer des hooks personnalisés en fonction de vos besoins :

ts
import { signalStore, withState, withHooks, type SignalStoreHooks } from '@ngrx/signals';

// Définir des hooks personnalisés
type MyCustomHooks = SignalStoreHooks & {
  afterDataLoaded?: (store: MyStore) => void;
};

// Utiliser les hooks personnalisés
export const MyStore = signalStore(
  withState({ data: null, isLoaded: false }),
  withHooks<typeof store, MyCustomHooks>({
    onInit(store) {
      loadData().then(data => {
        patchState(store, { data, isLoaded: true });
        
        // Appeler le hook personnalisé si défini
        if (this.afterDataLoaded) {
          this.afterDataLoaded(store);
        }
      });
    },
    
    // Hook personnalisé
    afterDataLoaded(store) {
      console.log('Data has been loaded', store.data());
    }
  })
);

Propagation des erreurs

Les erreurs levées dans les hooks du cycle de vie se propagent normalement, vous devez donc les gérer correctement :

ts
withHooks({
  onInit(store) {
    try {
      // Code qui pourrait lever une erreur
      const data = JSON.parse(localStorage.getItem('complexData') || '{}');
      patchState(store, { data });
    } catch (error) {
      console.error('Failed to initialize store', error);
      // Gérer l'erreur gracieusement
      patchState(store, { error: 'Erreur d\'initialisation' });
    }
  }
})

Partage de données entre hooks

Si vous avez besoin de partager des données entre différents hooks, vous pouvez utiliser des variables dans la portée de withHooks :

ts
withHooks((() => {
  // Variables privées partagées entre les hooks
  let subscription: Subscription | null = null;
  let lastUpdated: Date | null = null;
  
  return {
    onInit(store) {
      lastUpdated = new Date();
      subscription = someObservable.subscribe(data => {
        patchState(store, { data });
        lastUpdated = new Date();
      });
    },
    
    onDestroy() {
      if (subscription) {
        subscription.unsubscribe();
      }
      
      console.log('Last update was at:', lastUpdated);
    }
  };
})())