---
source_hash: "4ff98e61"
title: "SmartCommon Hooks"
weight: 130
---

# SmartCommon Hooks

SmartCommon provides a set of React hooks to facilitate application development.

## Global Hooks

These hooks use the application context and must be used within the `Provider`.

### useApi

API call management with automatic JWT authentication.

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

const api = useApi();

// Login
await api.login({ login, password, rememberMe: true });

// Logout
await api.logout();

// Authenticated request
const data = await api.private.get('items').json();

// Public request
const info = await api.public.get('public/info').json();
```

See [API Requests](/front/requetes-api) for more details.

### useGlobalStates

Global state with automatic persistence (localStorage/sessionStorage).

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

const gst = useGlobalStates();

// Read
const user = gst.get('user');
const theme = gst.get('settings.theme');

// Write (persistent)
gst.local.set('user', userData);

// Write (session only)
gst.session.set('tempData', data);

// Delete
gst.unset('user');

// Direct access to values
const { user, settings } = gst.values;
```

See [Data Storage](/front/stockage-de-donnees) for more details.

### useNavigation

Navigation utilities for react-router-dom.

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

const nav = useNavigation();

// Programmatic navigation
nav.navigate('/dashboard');
nav.navigate(-1); // Go back

// Route information
const { pathname, search, hash } = nav.location;

// URL parameters
const { id } = nav.params;
```

### useLibConfig

Access to application configuration.

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

const config = useLibConfig();

console.log(config.debug);     // true/false
console.log(config.api);       // { prefixUrl, timeout, ... }
```

### useConfirm

Confirmation and alert dialogs (requires ConfirmProvider).

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

const { confirm, alert } = useConfirm();

// Confirmation (returns true/false)
const handleDelete = async () => {
  const ok = await confirm({
    type: 'delete',        // 'danger' | 'delete' | 'warning' | 'info'
    title: 'Delete?',
    message: 'This action cannot be undone.',
    detail: item.label,    // optional detail
    confirmText: 'Delete',
    cancelText: 'Cancel',
  });

  if (ok) {
    await api.del(`items/${item.id}`);
  }
};

// Alert (single OK button)
await alert({
  type: 'info',
  title: 'Information',
  message: 'Operation completed successfully.',
});
```

### usePWAUpdate

PWA update management via Service Worker.

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

const { updateAvailable, checkForUpdates, applyUpdate } = usePWAUpdate({
  autoReload: false,
  checkInterval: 60000,
});
```

See [PWA](/front/pwa) for more details.

## Local Hooks

These hooks manage component-local state.

### useStates

Local state management with path notation.

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

const st = useStates({
  initialStates: {
    count: 0,
    user: { name: '', email: '' },
    items: []
  },
  debug: true
});

// Read
st.get('count');           // 0
st.get('user.name');       // ''
st.get('items[0]');        // undefined

// Write
st.set('count', 1);
st.set('user.name', 'John');
st.set('items[]', { id: 1 });  // Push to array

// Write with function
st.set('count', prev => prev + 1);

// Delete
st.unset('user.email');
st.unset('items[0]');

// Direct access
const { count, user, items } = st.values;
```

### useForm

Form management with state and errors.

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

const form = useForm({
  defaultValues: {
    name: '',
    email: ''
  },
  debug: true
});

// Read values
const name = form.get('values.name');

// Set a field with validation
form.setField({
  name: 'email',
  value: 'test@example.com',
  errors: {
    required: { condition: !value },
    format: { condition: !isValidEmail(value) }
  }
});

// Check errors
const hasEmailError = form.get('errors.email.required');

// Direct access
const { values, errors, isFormSubmitting, isFormSubmitted } = form;
```

### useDb

IndexedDB database via Dexie with automatic logging.

```javascript
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);
```

Automatically adds `createdAt`, `updatedAt` and a `logs` table.

See [Data Storage](/front/stockage-de-donnees) for more details.

> [!TIP]
> For API caching with strategies, prefer `useCachedQuery`. For offline synchronization, use `useSyncClient`.

### useCachedQuery

Query caching with network/cache strategies. This is the hook to use for data **read from API and frequently re-read**: dictionaries, configuration, reference lists.

> [!IMPORTANT]
> The exact name is `useCachedQuery`, with the `d`. `useCacheQuery` does not exist.

#### Declare the Cache Store

The hook stores its entries in a Dexie store that you must declare, indexed on `key`:

```javascript
// src/db/index.js
export const db = new Db({
  name: "myapp",
  version: 1,
  stores: {
    queryCache: 'key',
    // ... your other stores
  },
}).db;
```

#### Usage

```javascript
import { useCachedQuery, CACHE_STRATEGIES } from '@cap-rel/smartcommon';
import { db } from 'src/db';

const {
  data,         // data, from network or cache
  isLoading,    // true while fetching
  isFromCache,  // true if data comes from cache
  isStale,      // true if cached data is stale
  error,        // possible error
  lastFetch,    // timestamp of last successful fetch
  refetch,      // retry the request
  invalidate,   // clear cache and retry
} = useCachedQuery({
  db: db.instance,             // Dexie instance (db.instance, not db)
  store: 'queryCache',         // store name declared above
  key: 'countries',            // cache key, unique per request
  fetchFn: () => api.private.get('dictionaries/countries').json(),
  strategy: CACHE_STRATEGIES.CACHE_FIRST,
  ttl: 86400000,               // cache TTL, default 1h
  staleTime: 60000,            // stale threshold, default 1min
  enabled: true,               // disable request if false
});
```

#### Choose the Strategy

| Strategy | Behavior | When to Use |
| --- | --- | --- |
| `NETWORK_FIRST` (`network-first`) | network first, cache as fallback if request fails | default; business data that must be fresh but must remain readable offline |
| `CACHE_FIRST` (`cache-first`) | cache if still valid, network otherwise | dictionaries and references that rarely change (countries, units, types) |
| `STALE_WHILE_REVALIDATE` (`swr`) | display cache immediately, revalidate in background | configuration, preferences: display is instant and self-corrects |

#### ttl and staleTime, Do Not Confuse

| Option | Default | Effect |
| --- | --- | --- |
| `ttl` | 1 h | beyond this, entry is **deleted** and considered absent |
| `staleTime` | 1 min | beyond this, entry is still served but `isStale` becomes `true` |

So `staleTime` is always less than `ttl`. A dictionary type is set with a `ttl` of 24 hours.

#### Invalidate

```javascript
// After a write that makes cache obsolete
await api.private.post('items', { json: payload });
await invalidate();     // clear entry and retry request
```

`refetch()` retries the request without clearing cache: the cached value remains available if network fails.

#### Offline Behavior

The hook relies on `useOnlineStatus`. Offline, it serves cache if it exists and does not emit any request. Test `isFromCache` to notify the user they are consulting locally stored data.

```javascript
{isFromCache && isStale && (
  <span className="text-xs italic">Data saved on {formatDate(lastFetch)}</span>
)}
```

> [!TIP]
> `useCachedQuery` does not replace the business database. A list of objects that the user creates, modifies or deletes offline falls under `Db` and `useSyncClient`. `useCachedQuery` is designed for **read** data, not for produced data.

See [Data Storage](/front/stockage-de-donnees) and [Offline Synchronization](/front/synchronisation).

### useOnlineStatus

Network connection detection with optional health check.

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

const { isOnline, isServerReachable, lastOnline, checkNow } = useOnlineStatus({
  healthCheckUrl: '/api/health',
  healthCheckInterval: 30000,
  stabilityDelay: 2000,
  timeout: 5000,
});
```

### useAuthenticatedImage

Authenticated image loading with IndexedDB cache.

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

const { src, isLoading, isFromCache, error } = useAuthenticatedImage({
  db: db.instance,              // Dexie instance
  url: `/api/users/${id}/photo`,
  token: accessToken,
  ttl: 86400000,                // cache duration: 24h
  staleTime: 3600000,           // stale after 1h (background refresh)
  placeholder: '/images/default.png',
});

return <img src={src} alt="Photo" />;
```

## Utility Hooks

### useIntl

Date and number formatting with Intl API.

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

const intl = useIntl();

// Format a date
const formatted = intl.DateTimeFormat(Date.now());
// "01/11/2025, 14:30:00"

// With custom options
const dateOnly = intl.DateTimeFormat(Date.now(), 'fr-FR', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});
// "11 janvier 2025"
```

### useAnimation

Animation management with Framer Motion.

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

const { start, animations, setAnimations } = useAnimation({
  fadeIn: { value: false, state: null },
  slideIn: { value: false, state: null }
});

// start becomes true after first render
// Use it to trigger entry animations

useEffect(() => {
  if (start) {
    setAnimations(prev => ({
      ...prev,
      fadeIn: { ...prev.fadeIn, state: 'visible' }
    }));
  }
}, [start]);
```

### useFile

File management utilities.

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

const { resizeImage } = useFile();

// Resize an 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

Props merging with component variants (mainly internal use).

```javascript
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>
  );
};
```

| Property | Type | Description |
| --- | --- | --- |
| `variantProps` | object | Props merged from global theme + variant + props |
| `mergeProps` | function | `mergeProps(key, fn)`: merge props of an element/sub-component |
| `mergeQuickProps` | function | `mergeQuickProps(props, keys)`: extract a subset of variantProps |
| `setParams` | function | `setParams(params)`: set dynamic parameters for className functions |

### useListDnD

Drag and drop for lists (reordering). Takes the `set` function from `useStates` as parameter.

```javascript
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);

// Usage on each list element
{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>
))}
```

Callback parameters:

| Callback | Parameters | Description |
| --- | --- | --- |
| `onDragStart` | `(e, indexLabel, parent)` | `indexLabel`: state key for dragged index |
| `onDragOver` | `(e, indexLabel, index, listLabel, list)` | Reorder list in real time |
| `onDrop` | `(indexLabel)` | Reset index to null |

### useCalculator

Programmatic control of the `Calculator` component (requires a `Calculator` in the component tree).

```javascript
import { useCalculator, Calculator } from '@cap-rel/smartcommon';

const MyComponent = () => {
  const { isOpen, open, close, toggle } = useCalculator();

  return (
    <>
      <button onClick={() => open((result) => console.log('Result:', result))}>
        Open calculator
      </button>
      <Calculator />
    </>
  );
};
```

| Property | Type | Description |
| --- | --- | --- |
| `isOpen` | boolean | Calculator open state |
| `open` | function | `open(onResult?)`: open calculator with optional callback |
| `close` | function | Close calculator |
| `toggle` | function | Toggle open/close |

### useWindow

Browser window information.

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

const { orientation, windowDimension, scroll, darkMode } = useWindow();

// Dimensions
const { w, h } = windowDimension;

// Orientation: 'landscape' | 'portrait'
console.log(orientation);

// Scroll position
const { x, y } = scroll;

// System dark mode: true | false
console.log(darkMode);
```

### useIsDesktop

Responsive desktop/mobile detection.

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

const isDesktop = useIsDesktop();

if (isDesktop) {
  return <DesktopLayout />;
}
return <MobileLayout />;
```

### useStatesWorking

Simplified local state management with nested paths.

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

const { states, set, get, unset } = useStatesWorking({
  count: 0,
  user: { name: '', email: '' }
});

// Read
get('user.name');

// Write (path notation)
set('user.name', 'John');
set('items[0]', { id: 1 });
set('items.[]', newItem);  // Push to array

// Delete
unset('user.email');
```

> [!TIP]
> Similar to `useStates` but with a more direct API: initial values are passed directly, without `initialStates` wrapper.

### useSyncClient

Offline/online synchronization with conflict management.

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

const sync = useSyncClient({
  apiUrl: import.meta.env.VITE_API_URL,
  getAccessToken: () => api.accessToken,
  scope: ['items', 'categories']
});

// Connection state
const { isOnline, isServerReachable } = sync;

// Sync state
const { isInitialized, isSyncing, pendingCount, conflictsCount, lastSyncTime, syncError } = sync;

// Local CRUD operations (work offline)
await sync.create('items', { ref: 'IT-001', label: 'Item 1' });
await sync.update('items', id, { label: 'Updated item' });
await sync.remove('items', id);
await sync.upsert('items', { id, label: 'Create or update' });

// Local read
await sync.getEntity('items', id);
const items = await sync.queryEntities('items', { category: 'A' });

// Manual synchronization
await sync.sync();      // Push + Pull
await sync.push();      // Send local changes
await sync.pull();      // Fetch server changes

// Conflict management
const conflicts = await sync.getConflicts();
await sync.resolveConflict(conflictId, 'client'); // 'client' | 'server' | mergedData

// Device registration
await sync.register({ deviceName: 'My phone' });

// Reset
await sync.reset();
```

See [Synchronization](/front/synchronisation) for more details.

## Summary Table

| Hook | Category | Description |
| --- | --- | --- |
| `useApi` | Global | API calls with JWT auth |
| `useGlobalStates` | Global | Persistent global state |
| `useNavigation` | Global | react-router navigation |
| `useLibConfig` | Global | App configuration |
| `useConfirm` | Global | Confirmation dialogs |
| `usePWAUpdate` | Global | PWA updates |
| `useStates` | Local | Local state with path notation |
| `useStatesWorking` | Local | Simplified local state with paths |
| `useForm` | Local | Form management |
| `useCachedQuery` | Local | Data caching with strategies |
| `useOnlineStatus` | Local | Network connection detection |
| `useAuthenticatedImage` | Local | Authenticated images with cache |
| `useSyncClient` | Sync | Offline/online synchronization |
| `useIntl` | Utility | Date/number formatting |
| `useAnimation` | Utility | Framer Motion animations |
| `useFile` | Utility | File manipulation |
| `useVariantMerger` | Utility | Variant merging |
| `useCalculator` | Utility | Calculator component control |
| `useListDnD` | Utility | Drag and drop |
| `useWindow` | Utility | Window/orientation/scroll info |
| `useIsDesktop` | Utility | Desktop/mobile detection |

## See Also
- [SmartCommon](/front/smartcommon) - Component List
- [API Requests](/front/requetes-api) - Detailed useApi Documentation
- [Data Storage](/front/stockage-de-donnees) - Detailed Storage Documentation
