---
title: "Chapitre 3 : Hooks utilitaires"
weight: 600
---

# Chapitre 3 : Hooks utilitaires

SmartCommon fournit des hooks utilitaires pour les tâches courantes.

## useNavigation

Navigation programmatique avec React Router.

```javascript
import { useNavigation } from '@cap-rel/smartcommon';

function MyComponent() {
    const nav = useNavigation();

    return (
        <div>
            {/* Navigation vers une route */}
            <button onClick={() => nav.navigate('/products')}>
                Produits
            </button>

            {/* Navigation avec paramètre */}
            <button onClick={() => nav.navigate(`/products/${id}`)}>
                Détails
            </button>

            {/* Retour arrière */}
            <button onClick={() => nav.navigate(-1)}>
                Retour
            </button>

            {/* Remplacement (sans historique) */}
            <button onClick={() => nav.replace('/login').to()}>
                Login
            </button>
        </div>
    );
}
```

## useLibConfig

Accès à la configuration de l'application.

```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

```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

```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.

```javascript
import { useWindow } from '@cap-rel/smartcommon';

function ResponsiveLayout({ children }) {
    const { orientation, windowDimension, darkMode } = useWindow();

    // orientation: 'portrait' ou 'landscape'
    // windowDimension: { w, h }
    // darkMode: boolean

    if (windowDimension.w < 768) {
        return <MobileLayout>{children}</MobileLayout>;
    }

    if (windowDimension.w < 1024) {
        return <TabletLayout>{children}</TabletLayout>;
    }

    return <DesktopLayout>{children}</DesktopLayout>;
}

function WindowInfo() {
    const { windowDimension } = useWindow();

    return <p>Fenêtre : {windowDimension.w} x {windowDimension.h}</p>;
}
```

## useFile

Utilitaires pour la manipulation de fichiers.

### Redimensionner une image

```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

```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

```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

```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

```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.

```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.

```javascript
import { useListDnD } from '@cap-rel/smartcommon';

function SortableList({ initialItems, onReorder }) {
    const {
        items,
        onDragStart,
        onDragOver,
        onDrop
    } = useListDnD({
        initialItems,
        onReorder: (newItems) => {
            console.log('Nouvel ordre:', newItems);
            onReorder(newItems);
        }
    });

    return (
        <ul>
            {items.map((item, index) => (
                <li
                    key={item.id}
                    draggable
                    onDragStart={(e) => onDragStart(e, index)}
                    onDragOver={onDragOver}
                    onDrop={(e) => onDrop(e, index)}
                >
                    {item.name}
                </li>
            ))}
        </ul>
    );
}
```

## Tableau récapitulatif

| Hook | Usage | Exemple |
| --- | --- | --- |
| useNavigation | Navigation | nav.navigate('/products') |
| useLibConfig | Config app | config.debug |
| useIntl | Formatage | intl.DateTimeFormat(date) |
| useWindow | Responsive | orientation, windowDimension, scroll, darkMode |
| useFile | Fichiers | resizeImage(file, options) |
| useDb | IndexedDB | db.items.add(item) |
| useAnimation | Animations | start (boolean) |
| useListDnD | Drag & drop | onDragStart, onDragOver, onDrop |

## Points clés à retenir

1. **useNavigation** pour la navigation programmatique
2. **useIntl** pour le formatage localisé
3. **useWindow** pour le responsive design
4. **useFile** pour le traitement d'images
5. **useDb** pour le stockage local (offline)

[← Chapitre précédent](/training/module7-smartcommon-hooks/etat) | [Retour au module](/training/module7-smartcommon-hooks) | [Module suivant : Backend API ->](/training/module8-backend-api)
