Chapitre 3 : Hooks utilitaires
SmartCommon fournit des hooks utilitaires pour les tâches courantes.
useNavigation
Navigation programmatique avec React Router.
- snippet.javascript
import { useNavigation } from '@cap-rel/smartcommon'; function MyComponent() { const navigate = useNavigation(); return ( <div> {/* Navigation vers une route */} <button onClick={() => navigate('/products')}> Produits </button> {/* Navigation avec paramètre */} <button onClick={() => navigate(`/products/${id}`)}> Détails </button> {/* Retour arrière */} <button onClick={() => navigate(-1)}> Retour </button> {/* Remplacement (sans historique) */} <button onClick={() => navigate('/login', { replace: true })}> Login </button> </div> ); }
useLibConfig
Accès à la configuration de l'application.
- snippet.javascript
import { useLibConfig } from '@cap-rel/smartcommon'; function DebugInfo() { const config = useLibConfig(); if (!config.debug) return null; return ( <div className="debug-panel"> <p>API URL: {config.api.prefixUrl}</p> <p>Mode: {config.debug ? 'Debug' : 'Production'}</p> </div> ); }
useIntl
Formatage de dates et nombres avec l'API Intl.
Formatage de dates
- snippet.javascript
import { useIntl } from '@cap-rel/smartcommon'; function EventCard({ event }) { const intl = useIntl(); // Format par défaut const dateFormatted = intl.DateTimeFormat(event.date); // "15/03/2024, 14:30:00" // Format personnalisé const dateOnly = intl.DateTimeFormat(event.date, 'fr-FR', { year: 'numeric', month: 'long', day: 'numeric' }); // "15 mars 2024" // Heure seule const timeOnly = intl.DateTimeFormat(event.date, 'fr-FR', { hour: '2-digit', minute: '2-digit' }); // "14:30" return ( <div> <h3>{event.title}</h3> <p>Le {dateOnly} à {timeOnly}</p> </div> ); }
Formatage de nombres
- snippet.javascript
function PriceDisplay({ price, currency = 'EUR' }) { const intl = useIntl(); const formatted = intl.NumberFormat(price, 'fr-FR', { style: 'currency', currency: currency }); // "29,99 €" return <span>{formatted}</span>; } function PercentDisplay({ value }) { const intl = useIntl(); const formatted = intl.NumberFormat(value, 'fr-FR', { style: 'percent', minimumFractionDigits: 1 }); // "15,5 %" return <span>{formatted}</span>; }
useWindow
Informations sur la fenêtre du navigateur.
- snippet.javascript
import { useWindow } from '@cap-rel/smartcommon'; function ResponsiveLayout({ children }) { const { width, height, isMobile, isTablet, isDesktop } = useWindow(); if (isMobile) { return <MobileLayout>{children}</MobileLayout>; } if (isTablet) { return <TabletLayout>{children}</TabletLayout>; } return <DesktopLayout>{children}</DesktopLayout>; } function WindowInfo() { const { width, height } = useWindow(); return <p>Fenêtre : {width} x {height}</p>; }
useFile
Utilitaires pour la manipulation de fichiers.
Redimensionner une image
- snippet.javascript
import { useFile } from '@cap-rel/smartcommon'; function ImageUploader({ onUpload }) { const { resizeImage } = useFile(); const handleFileChange = async (e) => { const file = e.target.files[0]; if (!file) return; // Redimensionner avant upload const base64 = await resizeImage(file, { maxWidth: 1920, maxHeight: 1080, quality: 85 }); // base64 = "data:image/jpeg;base64,..." onUpload(base64); }; return ( <input type="file" accept="image/*" onChange={handleFileChange} /> ); }
useDb
Base de données IndexedDB via Dexie pour le stockage local de grandes quantités de données.
Configuration
- snippet.javascript
import { useDb } from '@cap-rel/smartcommon'; const db = useDb({ name: 'myApp', version: 1, stores: { items: 'id++, name, category, createdAt', logs: 'id++, action, timestamp' }, debug: true });
Opérations CRUD
- snippet.javascript
function ItemsManager() { const db = useDb({ name: 'myApp', version: 1, stores: { items: 'id++, name, category' } }); // Créer const addItem = async (item) => { const id = await db.items.add(item); console.log('Item créé avec id:', id); return id; }; // Lire tous const getAllItems = async () => { return db.items.toArray(); }; // Lire un const getItem = async (id) => { return db.items.get(id); }; // Modifier const updateItem = async (id, changes) => { await db.items.update(id, changes); }; // Supprimer const deleteItem = async (id) => { await db.items.delete(id); }; // ... }
Requêtes avancées
- snippet.javascript
// Filtrer par valeur const electronics = await db.items .where('category') .equals('electronics') .toArray(); // Filtrer par plage const expensiveItems = await db.items .where('price') .above(100) .toArray(); // Trier const sorted = await db.items .orderBy('createdAt') .reverse() .toArray(); // Limiter const first10 = await db.items .limit(10) .toArray(); // Compter const count = await db.items.count(); // Recherche texte const matching = await db.items .filter(item => item.name.includes('test')) .toArray();
Exemple : Mode offline
- snippet.javascript
import { useEffect } from 'react'; import { useApi, useDb, useStates } from '@cap-rel/smartcommon'; function OfflineCapableList() { const api = useApi(); const db = useDb({ name: 'myApp', version: 1, stores: { items: 'id, name, synced' } }); const st = useStates({ initialStates: { items: [], loading: true } }); useEffect(() => { loadItems(); }, []); const loadItems = async () => { try { // Essayer de charger depuis l'API const data = await api.private.get('items').json(); // Sauvegarder en local await db.items.clear(); await db.items.bulkAdd(data.map(i => ({ ...i, synced: true }))); st.set('items', data); } catch (err) { // En cas d'erreur réseau, charger depuis IndexedDB console.log('Mode offline - chargement local'); const localItems = await db.items.toArray(); st.set('items', localItems); } finally { st.set('loading', false); } }; const addItem = async (item) => { // Sauvegarder localement d'abord const id = await db.items.add({ ...item, synced: false }); // Mettre à jour l'UI st.set('items', [...st.get('items'), { ...item, id }]); // Tenter de synchroniser try { const result = await api.private.post('items', { json: item }).json(); await db.items.update(id, { ...result, synced: true }); } catch (err) { console.log('Sync échouée, sera retentée plus tard'); } }; // ... }
useAnimation
Gestion d'animations avec Framer Motion.
- snippet.javascript
import { useAnimation } from '@cap-rel/smartcommon'; import { motion } from 'framer-motion'; function AnimatedList({ items }) { const { start } = useAnimation(); return ( <div> {items.map((item, index) => ( <motion.div key={item.id} initial={{ opacity: 0, y: 20 }} animate={start ? { opacity: 1, y: 0 } : {}} transition={{ delay: index * 0.1 }} > {item.name} </motion.div> ))} </div> ); }
useListDnD
Drag and drop pour réordonner des listes.
- snippet.javascript
import { useListDnD } from '@cap-rel/smartcommon'; function SortableList({ initialItems, onReorder }) { const { items, handleDragStart, handleDragOver, handleDrop } = useListDnD({ initialItems, onReorder: (newItems) => { console.log('Nouvel ordre:', newItems); onReorder(newItems); } }); return ( <ul> {items.map((item, index) => ( <li key={item.id} draggable onDragStart={(e) => handleDragStart(e, index)} onDragOver={handleDragOver} onDrop={(e) => handleDrop(e, index)} > {item.name} </li> ))} </ul> ); }
Tableau récapitulatif
| Hook | Usage | Exemple |
|---|---|---|
| useNavigation | Navigation | navigate('/products') |
| useLibConfig | Config app | config.debug |
| useIntl | Formatage | intl.DateTimeFormat(date) |
| useWindow | Responsive | isMobile, width |
| useFile | Fichiers | resizeImage(file, options) |
| useDb | IndexedDB | db.items.add(item) |
| useAnimation | Animations | start (boolean) |
| useListDnD | Drag & drop | handleDragStart, handleDrop |
Points clés à retenir
- useNavigation pour la navigation programmatique
- useIntl pour le formatage localisé
- useWindow pour le responsive design
- useFile pour le traitement d'images
- useDb pour le stockage local (offline)
← Chapitre précédent | Retour au module | Module suivant : Backend API →