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.
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 for more details.
useGlobalStates
Global state with automatic persistence (localStorage/sessionStorage).
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 for more details.
useNavigation
Navigation utilities for react-router-dom.
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.
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).
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.
import { usePWAUpdate } from '@cap-rel/smartcommon';
const { updateAvailable, checkForUpdates, applyUpdate } = usePWAUpdate({
autoReload: false,
checkInterval: 60000,
});
See PWA for more details.
Local Hooks
These hooks manage component-local state.
useStates
Local state management with path notation.
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.
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.
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 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:
// src/db/index.js
export const db = new Db({
name: "myapp",
version: 1,
stores: {
queryCache: 'key',
// ... your other stores
},
}).db;
Usage
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
// 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.
{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 and Offline Synchronization.
useOnlineStatus
Network connection detection with optional health check.
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.
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.
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.
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.
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).
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.
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).
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.
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.
import { useIsDesktop } from '@cap-rel/smartcommon';
const isDesktop = useIsDesktop();
if (isDesktop) {
return <DesktopLayout />;
}
return <MobileLayout />;
useStatesWorking
Simplified local state management with nested paths.
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.
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 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 - Component List
- API Requests - Detailed useApi Documentation
- Data Storage - Detailed Storage Documentation