# 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]]