# Module 11 : Bonnes pratiques
> Ce module final résume les bonnes pratiques pour le développement d'applications SmartMaker.
## Organisation du code
### Structure des composants
```
components/pages/private/TasksPage/
├── index.jsx # Composant principal (export)
├── TasksPage.jsx # Logique et rendu
├── components/ # Sous-composants locaux
│ ├── TaskList.jsx
│ └── TaskFilters.jsx
└── hooks/ # Hooks spécifiques à cette page
└── useTasks.js
```
### Convention de nommage
- **Composants** : PascalCase (`TaskList`, `UserProfile`)
- **Hooks** : camelCase avec préfixe use (`useTasks`, `useAuth`)
- **Fichiers** : même nom que le composant (`TaskList.jsx`)
- **Dossiers** : PascalCase pour composants, camelCase pour hooks
## Performance
### Éviter les re-rendus inutiles
```javascript
// Éviter : objet recréé à chaque rendu
// Préférer : objet stable
const style = useMemo(() => ({ color: 'red' }), []);
```
### Mémoriser les callbacks
```javascript
// Pour les composants mémorisés
const handleClick = useCallback((id) => {
setSelected(id);
}, []);
```
### Lazy loading des pages
```javascript
import { lazy, Suspense } from 'react';
import { Spinner } from '@cap-rel/smartcommon';
const TasksPage = lazy(() => import('./pages/private/TasksPage'));
}>
```
## Gestion d'état
### Choisir le bon outil
^ Besoin ^ Solution ^
| État d'un formulaire | useStates ou useForm |
| État d'une page (loading, error) | useStates |
| Session utilisateur | useGlobalStates('session') |
| Préférences | useGlobalStates('settings') |
| Données partagées | useGlobalStates |
| Gros volumes, offline | useDb |
### Structurer l'état global
```javascript
globalState: {
reducers: {
// Auth
session: null,
// Préférences
settings: { lng: 'fr', theme: 'light' },
// Cache de données
cache: {
categories: [],
lastFetch: null
}
}
}
```
## Appels API
### Toujours gérer les erreurs
```javascript
const loadData = async () => {
st.set('loading', true);
st.set('error', null);
try {
const data = await api.private.get('items').json();
st.set('items', data.items);
} catch (err) {
st.set('error', err.message);
// Log pour debug
console.error('Load error:', err);
} finally {
st.set('loading', false);
}
};
```
### Annuler les requêtes au démontage
```javascript
useEffect(() => {
const controller = new AbortController();
const load = async () => {
try {
const data = await api.private.get('items', {
signal: controller.signal
}).json();
setItems(data);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
}
};
load();
return () => controller.abort();
}, []);
```
## Formulaires
### Validation avec Zod
```javascript
import { z } from 'zod';
const schema = z.object({
email: z.string().email('Email invalide'),
password: z.string().min(8, 'Minimum 8 caractères'),
age: z.number().min(18, 'Doit être majeur').optional()
});
```
### Messages d'erreur clairs
```javascript
```
## Sécurité
### Ne jamais stocker de secrets côté client
```javascript
// JAMAIS
const API_KEY = 'secret123';
// OK : variables d'environnement serveur uniquement
// Le client n'a accès qu'aux VITE_ prefixés
const API_URL = import.meta.env.VITE_API_URL;
```
### Valider côté serveur
Ne pas faire confiance aux données du client :
```php
// Controller PHP
public function create($payload)
{
// Toujours valider
if (empty($payload['label'])) {
return ['Label required', 400];
}
// Toujours vérifier les droits
if (!$user->hasRight('mymodule', 'create')) {
return ['Forbidden', 403];
}
// Toujours échapper
$label = $db->escape($payload['label']);
}
```
### Protéger contre XSS
React échappe automatiquement, mais attention à `dangerouslySetInnerHTML` :
```javascript
// Dangereux
// Si nécessaire, sanitiser d'abord
import DOMPurify from 'dompurify';
```
## Tests
### Tester les composants critiques
```javascript
// TasksPage.test.jsx
import { render, screen, waitFor } from '@testing-library/react';
import { TasksPage } from './TasksPage';
test('affiche la liste des tâches', async () => {
render();
await waitFor(() => {
expect(screen.getByText('Mes tâches')).toBeInTheDocument();
});
});
```
## Debug
### Utiliser les outils
- **React DevTools** : inspecter les composants et l'état
- **Redux DevTools** : voir les actions et l'état global
- **Console** : st.set avec debug: true
```javascript
const st = useStates({
initialStates: { ... },
debug: import.meta.env.DEV // Logs uniquement en dev
});
```
## Checklist avant déploiement
- [ ] Toutes les erreurs sont gérées
- [ ] Les chargements affichent un spinner
- [ ] La validation fonctionne côté client ET serveur
- [ ] Les droits sont vérifiés côté serveur
- [ ] Le mode offline fonctionne (si applicable)
- [ ] Les traductions sont complètes
- [ ] Les performances sont acceptables
- [ ] HTTPS est activé
- [ ] Les variables d'environnement de prod sont configurées
## Ressources
- Documentation React : https://react.dev
- Documentation SmartCommon : https://inligit.fr/cap-rel/dolibarr/smartmaker/smartcommon
- Documentation Dolibarr : https://wiki.dolibarr.org
[[:15_training:module10-fonctionnalites-avancees:autres|← Chapitre précédent]] | [[:15_training:start|Retour à l'index de la formation]]