Chapter 3: Utility Hooks
SmartCommon provides utility hooks for common tasks.
useNavigation
Programmatic navigation with React Router.
import { useNavigation } from '@cap-rel/smartcommon';
function MyComponent() {
const nav = useNavigation();
return (
<div>
{/* Navigate to route */}
<button onClick={() => nav.navigate('/products')}>
Products
</button>
{/* Navigate with parameter */}
<button onClick={() => nav.navigate(`/products/${id}`)}>
Details
</button>
{/* Go back */}
<button onClick={() => nav.navigate(-1)}>
Back
</button>
{/* Replace (no history) */}
<button onClick={() => nav.replace('/login')}>
Login
</button>
</div>
);
}
useLibConfig
Access application configuration.
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
Date and number formatting with the Intl API.
Date Formatting
import { useIntl } from '@cap-rel/smartcommon';
function EventCard({ event }) {
const intl = useIntl();
// Default format
const dateFormatted = intl.DateTimeFormat(event.date);
// "03/15/2024, 2:30:00 PM"
// Custom format
const dateOnly = intl.DateTimeFormat(event.date, 'fr-FR', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
// "15 mars 2024"
// Time only
const timeOnly = intl.DateTimeFormat(event.date, 'fr-FR', {
hour: '2-digit',
minute: '2-digit'
});
// "2:30 PM"
return (
<div>
<h3>{event.title}</h3>
<p>On {dateOnly} at {timeOnly}</p>
</div>
);
}
Number Formatting
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
Browser window information.
import { useWindow } from '@cap-rel/smartcommon';
function ResponsiveLayout({ children }) {
const { orientation, windowDimension, darkMode } = useWindow();
// orientation: 'portrait' or '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>Window: {windowDimension.w} x {windowDimension.h}</p>;
}
useFile
File manipulation utilities.
Resize Image
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;
// Resize before 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
IndexedDB database via Dexie for local storage of large amounts of data.
Configuration
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
});
CRUD Operations
function ItemsManager() {
const db = useDb({
name: 'myApp',
version: 1,
stores: {
items: 'id++, name, category'
}
});
// Create
const addItem = async (item) => {
const id = await db.items.add(item);
console.log('Item created with id:', id);
return id;
};
// Read all
const getAllItems = async () => {
return db.items.toArray();
};
// Read one
const getItem = async (id) => {
return db.items.get(id);
};
// Update
const updateItem = async (id, changes) => {
await db.items.update(id, changes);
};
// Delete
const deleteItem = async (id) => {
await db.items.delete(id);
};
// ...
}
Advanced Queries
// Filter by value
const electronics = await db.items
.where('category')
.equals('electronics')
.toArray();
// Filter by range
const expensiveItems = await db.items
.where('price')
.above(100)
.toArray();
// Sort
const sorted = await db.items
.orderBy('createdAt')
.reverse()
.toArray();
// Limit
const first10 = await db.items
.limit(10)
.toArray();
// Count
const count = await db.items.count();
// Text search
const matching = await db.items
.filter(item => item.name.includes('test'))
.toArray();
Example: Offline Mode
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 {
// Try to load from API
const data = await api.private.get('items').json();
// Save locally
await db.items.clear();
await db.items.bulkAdd(data.map(i => ({ ...i, synced: true })));
st.set('items', data);
} catch (err) {
// On network error, load from IndexedDB
console.log('Offline mode - loading local');
const localItems = await db.items.toArray();
st.set('items', localItems);
} finally {
st.set('loading', false);
}
};
const addItem = async (item) => {
// Save locally first
const id = await db.items.add({ ...item, synced: false });
// Update UI
st.set('items', [...st.get('items'), { ...item, id }]);
// Try to sync
try {
const result = await api.private.post('items', { json: item }).json();
await db.items.update(id, { ...result, synced: true });
} catch (err) {
console.log('Sync failed, will retry later');
}
};
// ...
}
useAnimation
Animation management with Framer Motion.
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 for reordering lists.
import { useListDnD } from '@cap-rel/smartcommon';
function SortableList({ initialItems, onReorder }) {
const {
items,
onDragStart,
onDragOver,
onDrop
} = useListDnD({
initialItems,
onReorder: (newItems) => {
console.log('New order:', 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>
);
}
Summary Table
| Hook | Usage | Example |
|---|---|---|
| useNavigation | Navigation | nav.navigate('/products') |
| useLibConfig | App config | config.debug |
| useIntl | Formatting | intl.DateTimeFormat(date) |
| useWindow | Responsive | orientation, windowDimension, scroll, darkMode |
| useFile | Files | resizeImage(file, options) |
| useDb | IndexedDB | db.items.add(item) |
| useAnimation | Animations | start (boolean) |
| useListDnD | Drag & drop | onDragStart, onDragOver, onDrop |
Key Points to Remember
- useNavigation for programmatic navigation
- useIntl for localized formatting
- useWindow for responsive design
- useFile for image processing
- useDb for local storage (offline)
Previous Chapter | Back to Module | Next Module: Backend API ->