React - Bonnes Pratiques

Guide des meilleures pratiques pour écrire du code React propre, performant et maintenable.

Structure des composants

✅ Bonnes pratiques

// Composants fonctionnels
function UserProfile({ user }) {
  return (
    <div className="profile">
      <h2>{user.name}</h2>
      <UserAvatar user={user} />
      <UserInfo user={user} />
    </div>
  );
}

// Props avec valeurs par défaut
function Button({ type = 'button', children }) {
  return (
    <button type={type}>
      {children}
    </button>
  );
}

// Destructuration des props
function UserCard({ name, email, avatar }) {
  return (
    <div>
      <img src={avatar} alt={name} />
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  );
}

❌ À éviter

// Ne pas modifier les props
function BadComponent(props) {
  props.value = 123; // ❌ Mauvais!
}

// Éviter les composants trop complexes
function HugeComponent() {
  // ❌ Trop de logique et d'état
  const [state1, setState1] = useState();
  const [state2, setState2] = useState();
  const [state3, setState3] = useState();
  // ...plus de 100 lignes de code
}

Gestion de l'état

✅ Bonnes pratiques

// État local simple
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      {count}
    </button>
  );
}

// État complexe avec reducer
function TodoList() {
  const [todos, dispatch] = useReducer(todoReducer, []);

  // Actions bien définies
  const addTodo = useCallback((text) => {
    dispatch({ type: 'ADD_TODO', payload: text });
  }, []);
}

❌ À éviter

// Ne pas mettre à jour l'état directement
const [items, setItems] = useState([]);
items.push(newItem); // ❌ Mauvais!

// Éviter les mises à jour synchrones multiples
setCount(count + 1);
setCount(count + 1); // ❌ Ne fonctionne pas

Performance

Optimisation des rendus

// Mémoisation des composants
const MemoizedComponent = React.memo(
  function ExpensiveComponent({ data }) {
    return <div>{/* Rendu coûteux */}</div>
  }
);

// Mémoisation des calculs
function DataList({ items }) {
  const sortedItems = useMemo(
    () => items.sort((a, b) => b - a),
    [items]
  );

  const handleClick = useCallback(
    () => {
      // Action
    },
    []
  );
}

Chargement optimisé

// Code splitting
const LazyComponent = React.lazy(
  () => import('./LazyComponent')
);

function App() {
  return (
    <Suspense fallback={<Loader />}>
      <LazyComponent />
    </Suspense>
  );
}

// Pagination et chargement virtualisé
import { VirtualScroller } from 'react-virtual';

Organisation du code

src/
├── components/         # Composants réutilisables
│   ├── Button/
│   │   ├── index.js
│   │   ├── Button.js
│   │   └── Button.css
│   └── Card/
├── pages/             # Pages/Routes
├── hooks/             # Hooks personnalisés
├── context/           # Contextes React
├── utils/             # Fonctions utilitaires
├── services/          # Appels API
└── assets/            # Images, fonts, etc.

Tests

// Test de composant
import { render, fireEvent } from '@testing-library/react';

test('bouton incrémente le compteur', () => {
  const { getByText } = render(<Counter />);
  const button = getByText('0');

  fireEvent.click(button);
  expect(button).toHaveTextContent('1');
});

// Test de hook personnalisé
import { renderHook, act } from '@testing-library/react-hooks';

Conventions de nommage

  • Composants : PascalCase (UserProfile, Button)
  • Hooks : camelCase commençant par "use" (useState, useEffect)
  • Événements : handle + Action (handleClick, handleSubmit)
  • Props : camelCase (onClick, isVisible)
  • Constants : UPPER_SNAKE_CASE (API_URL, MAX_ITEMS)