Hooks SmartCommon
SmartCommon fournit un ensemble de hooks React pour faciliter le développement d'applications.
Hooks globaux
Ces hooks utilisent le contexte de l'application et doivent être utilisés à l'intérieur du Provider.
useApi
Gestion des appels API avec authentification JWT automatique.
import { useApi } from '@cap-rel/smartcommon';
const api = useApi();
// Connexion
await api.login({ login, password, rememberMe: true });
// Déconnexion
await api.logout();
// Requête authentifiée
const data = await api.private.get('items').json();
// Requête publique
const info = await api.public.get('public/info').json();
Voir Requêtes API pour plus de détails.
useGlobalStates
État global avec persistance automatique (localStorage/sessionStorage).
import { useGlobalStates } from '@cap-rel/smartcommon';
const gst = useGlobalStates();
// Lire
const user = gst.get('user');
const theme = gst.get('settings.theme');
// Écrire (persistant)
gst.local.set('user', userData);
// Écrire (session uniquement)
gst.session.set('tempData', data);
// Supprimer
gst.unset('user');
// Accès direct aux valeurs
const { user, settings } = gst.values;
Voir Stockage de données pour plus de détails.
useNavigation
Utilitaires de navigation pour react-router-dom.
import { useNavigation } from '@cap-rel/smartcommon';
const nav = useNavigation();
// Navigation programmatique
nav.navigate('/dashboard');
nav.navigate(-1); // Retour arrière
// Informations de route
const { pathname, search, hash } = nav.location;
// Paramètres d'URL
const { id } = nav.params;
useLibConfig
Accès à la configuration de l'application.
import { useLibConfig } from '@cap-rel/smartcommon';
const config = useLibConfig();
console.log(config.debug); // true/false
console.log(config.api); // { prefixUrl, timeout, ... }
useConfirm
Dialogues de confirmation et d'alerte (nécessite ConfirmProvider).
import { useConfirm } from '@cap-rel/smartcommon';
const { confirm, alert } = useConfirm();
// Confirmation (retourne true/false)
const handleDelete = async () => {
const ok = await confirm({
type: 'delete', // 'danger' | 'delete' | 'warning' | 'info'
title: 'Supprimer ?',
message: 'Cette action est irréversible.',
detail: item.label, // détail optionnel
confirmText: 'Supprimer',
cancelText: 'Annuler',
});
if (ok) {
await api.del(`items/${item.id}`);
}
};
// Alerte (un seul bouton OK)
await alert({
type: 'info',
title: 'Information',
message: 'Opération terminée avec succès.',
});
usePWAUpdate
Gestion des mises à jour PWA via Service Worker.
import { usePWAUpdate } from '@cap-rel/smartcommon';
const { updateAvailable, checkForUpdates, applyUpdate } = usePWAUpdate({
autoReload: false,
checkInterval: 60000,
});
Voir PWA pour plus de détails.
Hooks locaux
Ces hooks gèrent un état local au composant.
useStates
Gestion d'état local avec path notation.
import { useStates } from '@cap-rel/smartcommon';
const st = useStates({
initialStates: {
count: 0,
user: { name: '', email: '' },
items: []
},
debug: true
});
// Lire
st.get('count'); // 0
st.get('user.name'); // ''
st.get('items[0]'); // undefined
// Écrire
st.set('count', 1);
st.set('user.name', 'John');
st.set('items[]', { id: 1 }); // Push dans le tableau
// Écrire avec fonction
st.set('count', prev => prev + 1);
// Supprimer
st.unset('user.email');
st.unset('items[0]');
// Accès direct
const { count, user, items } = st.values;
useForm
Gestion de formulaires avec état et erreurs.
import { useForm } from '@cap-rel/smartcommon';
const form = useForm({
defaultValues: {
name: '',
email: ''
},
debug: true
});
// Lire les valeurs
const name = form.get('values.name');
// Définir un champ avec validation
form.setField({
name: 'email',
value: 'test@example.com',
errors: {
required: { condition: !value },
format: { condition: !isValidEmail(value) }
}
});
// Vérifier les erreurs
const hasEmailError = form.get('errors.email.required');
// Accès direct
const { values, errors, isFormSubmitting, isFormSubmitted } = form;
useDb
Base de données IndexedDB via Dexie avec logging automatique.
import { useDb } from '@cap-rel/smartcommon';
const db = useDb({
name: 'myApp',
version: 1,
stores: {
items: 'id++, name, category, createdAt',
categories: 'id++, name'
},
debug: true
});
// CRUD via Dexie
await db.items.add({ name: 'Item 1' });
const all = await db.items.toArray();
await db.items.update(id, { name: 'Updated' });
await db.items.delete(id);
Ajoute automatiquement createdAt, updatedAt et une table logs.
Voir Stockage de données pour plus de détails.
Astuce
Pour un cache API avec stratégies, préférez useCachedQuery. Pour la synchronisation offline, utilisez useSyncClient.
useCachedQuery
Cache de requêtes avec stratégies réseau/cache. C'est le hook à utiliser pour une donnée lue depuis l'API et relue souvent : dictionnaires, configuration, listes de référence.
Important
Le nom exact est useCachedQuery, avec le d. useCacheQuery n'existe pas.
Déclarer le store de cache
Le hook stocke ses entrées dans un store Dexie que vous devez déclarer, indexé sur key :
// src/db/index.js
export const db = new Db({
name: "myapp",
version: 1,
stores: {
queryCache: 'key',
// ... vos autres stores
},
}).db;
Utilisation
import { useCachedQuery, CACHE_STRATEGIES } from '@cap-rel/smartcommon';
import { db } from 'src/db';
const {
data, // donnee, du reseau ou du cache
isLoading, // true pendant la recuperation
isFromCache, // true si la donnee vient du cache
isStale, // true si la donnee cachee est perimee
error, // erreur eventuelle
lastFetch, // timestamp de la derniere recuperation reussie
refetch, // relance la requete
invalidate, // vide le cache et relance
} = useCachedQuery({
db: db.instance, // instance Dexie (db.instance, pas db)
store: 'queryCache', // nom du store declare ci-dessus
key: 'countries', // cle de cache, unique par requete
fetchFn: () => api.private.get('dictionaries/countries').json(),
strategy: CACHE_STRATEGIES.CACHE_FIRST,
ttl: 86400000, // duree de vie du cache, defaut 1h
staleTime: 60000, // seuil de peremption, defaut 1min
enabled: true, // desactive la requete si false
});
Choisir la stratégie
| Stratégie | Comportement | Quand l'utiliser |
|---|---|---|
NETWORK_FIRST (network-first) |
réseau d'abord, cache en secours si la requête échoue | défaut ; données métier qui doivent être fraîches mais doivent rester lisibles hors ligne |
CACHE_FIRST (cache-first) |
cache si encore valide, réseau sinon | dictionnaires et référentiels qui changent rarement (pays, unités, types) |
STALE_WHILE_REVALIDATE (swr) |
affiche le cache immédiatement, revalide en arrière-plan | configuration, préférences : l'affichage est instantané et se corrige tout seul |
ttl et staleTime, à ne pas confondre
| Option | Défaut | Effet |
|---|---|---|
ttl |
1 h | au-delà, l'entrée est supprimée et considérée comme absente |
staleTime |
1 min | au-delà, l'entrée reste servie mais isStale passe à true |
staleTime est donc toujours inférieur à ttl. Un dictionnaire type se règle sur un ttl de 24 h.
Invalider
// Apres une ecriture qui rend le cache obsolete
await api.private.post('items', { json: payload });
await invalidate(); // vide l'entree et relance la requete
refetch() relance la requête sans vider le cache : la valeur cachée reste disponible si le réseau échoue.
Comportement hors ligne
Le hook s'appuie sur useOnlineStatus. Hors ligne, il sert le cache s'il existe et n'émet aucune requête. Testez isFromCache pour signaler à l'utilisateur qu'il consulte une donnée stockée localement.
{isFromCache && isStale && (
<span className="text-xs italic">Données enregistrées le {formatDate(lastFetch)}</span>
)}
Astuce
useCachedQuery ne remplace pas la base métier. Une liste d'objets que l'utilisateur crée, modifie ou supprime hors ligne relève de Db et de useSyncClient. useCachedQuery est fait pour de la donnée lue, pas pour de la donnée produite.
Voir Stockage de données et Synchronisation offline.
useOnlineStatus
Détection de la connexion réseau avec health check optionnel.
import { useOnlineStatus } from '@cap-rel/smartcommon';
const { isOnline, isServerReachable, lastOnline, checkNow } = useOnlineStatus({
healthCheckUrl: '/api/health',
healthCheckInterval: 30000,
stabilityDelay: 2000,
timeout: 5000,
});
useAuthenticatedImage
Chargement d'images authentifiées avec cache IndexedDB.
import { useAuthenticatedImage } from '@cap-rel/smartcommon';
const { src, isLoading, isFromCache, error } = useAuthenticatedImage({
db: db.instance, // instance Dexie
url: `/api/users/${id}/photo`,
token: accessToken,
ttl: 86400000, // durée de cache: 24h
staleTime: 3600000, // stale après 1h (refresh en arrière-plan)
placeholder: '/images/default.png',
});
return <img src={src} alt="Photo" />;
Hooks utilitaires
useIntl
Formatage de dates et nombres avec l'API Intl.
import { useIntl } from '@cap-rel/smartcommon';
const intl = useIntl();
// Formater une date
const formatted = intl.DateTimeFormat(Date.now());
// "11/01/2025, 14:30:00"
// Avec options personnalisées
const dateOnly = intl.DateTimeFormat(Date.now(), 'fr-FR', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
// "11 janvier 2025"
useAnimation
Gestion d'animations avec Framer Motion.
import { useAnimation } from '@cap-rel/smartcommon';
const { start, animations, setAnimations } = useAnimation({
fadeIn: { value: false, state: null },
slideIn: { value: false, state: null }
});
// start devient true après le premier rendu
// Utilisez-le pour déclencher des animations d'entrée
useEffect(() => {
if (start) {
setAnimations(prev => ({
...prev,
fadeIn: { ...prev.fadeIn, state: 'visible' }
}));
}
}, [start]);
useFile
Utilitaires pour la gestion de fichiers.
import { useFile } from '@cap-rel/smartcommon';
const { resizeImage } = useFile();
// Redimensionner une image
const handleFileChange = async (e) => {
const file = e.target.files[0];
const base64 = await resizeImage(file, {
maxWidth: 1920,
maxHeight: 1080,
quality: 85
});
console.log(base64); // data:image/jpeg;base64,...
};
useVariantMerger
Fusion de props avec variants de composants (usage interne principalement).
import { useVariantMerger } from '@cap-rel/smartcommon';
const MyComponent = (props) => {
const { variantProps, mergeProps, mergeQuickProps, setParams } = useVariantMerger('MyComponent', props);
return (
<div {...mergeProps('container', p => ({
...p,
className: `base-class ${p.className || ''}`
}))}>
{variantProps.children}
</div>
);
};
| Propriété | Type | Description |
|---|---|---|
variantProps |
object | Props fusionnées depuis le thème global + variant + props |
mergeProps |
function | mergeProps(key, fn) : fusionne les props d'un élément/sous-composant |
mergeQuickProps |
function | mergeQuickProps(props, keys) : extrait un sous-ensemble de variantProps |
setParams |
function | setParams(params) : définit des paramètres dynamiques pour les fonctions className |
useListDnD
Drag and drop pour listes (réordonnancement). Prend en paramètre la fonction set de useStates.
import { useStates, useListDnD } from '@cap-rel/smartcommon';
const st = useStates({
initialStates: {
items: [{ id: 1, label: 'A' }, { id: 2, label: 'B' }],
dragIndex: null,
}
});
const { onDragStart, onDragOver, onDrop } = useListDnD(st.set);
// Utilisation sur chaque élément de liste
{st.get('items').map((item, index) => (
<div
key={item.id}
draggable
onDragStart={(e) => onDragStart(e, 'dragIndex', 'items')}
onDragOver={(e) => onDragOver(e, 'dragIndex', index, 'items', st.get('items'))}
onDrop={() => onDrop('dragIndex')}
>
{item.label}
</div>
))}
Paramètres des callbacks :
| Callback | Paramètres | Description |
|---|---|---|
onDragStart |
(e, indexLabel, parent) |
indexLabel : clé d'état pour l'index glissé |
onDragOver |
(e, indexLabel, index, listLabel, list) |
Réordonne la liste en temps réel |
onDrop |
(indexLabel) |
Réinitialise l'index à null |
useCalculator
Contrôle programmatique du composant Calculator (nécessite un Calculator dans l'arbre de composants).
import { useCalculator, Calculator } from '@cap-rel/smartcommon';
const MyComponent = () => {
const { isOpen, open, close, toggle } = useCalculator();
return (
<>
<button onClick={() => open((result) => console.log('Résultat:', result))}>
Ouvrir la calculatrice
</button>
<Calculator />
</>
);
};
| Propriété | Type | Description |
|---|---|---|
isOpen |
boolean | État d'ouverture de la calculatrice |
open |
function | open(onResult?) : ouvre la calculatrice avec callback optionnel |
close |
function | Ferme la calculatrice |
toggle |
function | Bascule l'ouverture/fermeture |
useWindow
Informations sur la fenêtre du navigateur.
import { useWindow } from '@cap-rel/smartcommon';
const { orientation, windowDimension, scroll, darkMode } = useWindow();
// Dimensions
const { w, h } = windowDimension;
// Orientation : 'landscape' | 'portrait'
console.log(orientation);
// Position de scroll
const { x, y } = scroll;
// Mode sombre du système : true | false
console.log(darkMode);
useIsDesktop
Détection responsive desktop/mobile.
import { useIsDesktop } from '@cap-rel/smartcommon';
const isDesktop = useIsDesktop();
if (isDesktop) {
return <DesktopLayout />;
}
return <MobileLayout />;
useStatesWorking
Gestion d'état local simplifiée avec paths imbriqués.
import { useStatesWorking } from '@cap-rel/smartcommon';
const { states, set, get, unset } = useStatesWorking({
count: 0,
user: { name: '', email: '' }
});
// Lire
get('user.name');
// Écrire (path notation)
set('user.name', 'John');
set('items[0]', { id: 1 });
set('items.[]', newItem); // Push dans le tableau
// Supprimer
unset('user.email');
Astuce
Similaire à useStates mais avec une API plus directe : les valeurs initiales sont passées directement, sans wrapper initialStates.
useSyncClient
Synchronisation offline/online avec gestion de conflits.
import { useSyncClient } from '@cap-rel/smartcommon';
const sync = useSyncClient({
apiUrl: import.meta.env.VITE_API_URL,
getAccessToken: () => api.accessToken,
scope: ['items', 'categories']
});
// État de connexion
const { isOnline, isServerReachable } = sync;
// État de synchronisation
const { isInitialized, isSyncing, pendingCount, conflictsCount, lastSyncTime, syncError } = sync;
// Opérations CRUD locales (fonctionnent hors-ligne)
await sync.create('items', { ref: 'IT-001', label: 'Item 1' });
await sync.update('items', id, { label: 'Item modifié' });
await sync.remove('items', id);
await sync.upsert('items', { id, label: 'Créer ou modifier' });
// Lecture locale
const item = await sync.getEntity('items', id);
const items = await sync.queryEntities('items', { category: 'A' });
// Synchronisation manuelle
await sync.sync(); // Push + Pull
await sync.push(); // Envoyer les changements locaux
await sync.pull(); // Récupérer les changements serveur
// Gestion des conflits
const conflicts = await sync.getConflicts();
await sync.resolveConflict(conflictId, 'client'); // 'client' | 'server' | mergedData
// Enregistrement de l'appareil
await sync.register({ deviceName: 'Mon téléphone' });
// Réinitialisation
await sync.reset();
Voir Synchronisation pour plus de détails.
Tableau récapitulatif
| Hook | Catégorie | Description |
|---|---|---|
useApi |
Global | Appels API avec auth JWT |
useGlobalStates |
Global | État global persistant |
useNavigation |
Global | Navigation react-router |
useLibConfig |
Global | Configuration de l'app |
useConfirm |
Global | Dialogues de confirmation |
usePWAUpdate |
Global | Mises à jour PWA |
useStates |
Local | État local avec path notation |
useStatesWorking |
Local | État local simplifié avec paths |
useForm |
Local | Gestion de formulaires |
useCachedQuery |
Local | Cache de données avec stratégies |
useOnlineStatus |
Local | Détection connexion réseau |
useAuthenticatedImage |
Local | Images authentifiées avec cache |
useSyncClient |
Sync | Synchronisation offline/online |
useIntl |
Utilitaire | Formatage dates/nombres |
useAnimation |
Utilitaire | Animations Framer Motion |
useFile |
Utilitaire | Manipulation de fichiers |
useVariantMerger |
Utilitaire | Fusion de variants |
useCalculator |
Utilitaire | Contrôle du composant Calculator |
useListDnD |
Utilitaire | Drag and drop |
useWindow |
Utilitaire | Infos fenêtre/orientation/scroll |
useIsDesktop |
Utilitaire | Détection desktop/mobile |
Voir aussi
- SmartCommon - Liste des composants
- Requêtes API - Documentation détaillée useApi
- Stockage de données - Documentation détaillée stockage