Pourquoi hooks React essentiels ?
En tant que développeur senior React avec plus de 10 ans d'expérience, vous savez bien que les hooks sont des fonctionnalités puissantes et pratiques qui ont révolutionné le développement front-end. Ils offrent une flexibilité accrue sans compromettre la performance ou la modularité du code.
Un cas concret est lorsque vous devez gérer le state global dans une application React sans utiliser de bibliothèques tierces comme Redux. Les hooks, en particulier useContext et useReducer, offrent une solution naturelle pour ce besoin, simplifiant ainsi la gestion des états complexes.
Prerequis
- Connaissance approfondie de React avec JSX
- Familiarité avec les concepts de state et de props
- Maîtrise des fonctions et des callbacks en JavaScript
- Installation de Node.js (v14.x ou plus)
- Éditeur de code moderne comme VSCode
Concepts fondamentaux
1. useState
Le hook useState permet d'ajouter du state local à un composant fonction.
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
Count: {count}
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
2. useEffect
Le hook useEffect permet d'exécuter des effets secondaires dans les composants fonction.
import React, { useState, useEffect } from 'react';
function Example() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<div>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : 'Loading...'}
</div>
);
}
3. useContext
Le hook useContext permet d'accéder aux valeurs du contexte.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style=backgroundColor: theme === 'dark' ? 'black' : 'white', color: theme === 'dark' ? 'white' : 'black'>
Click me
</button>
);
}
4. useReducer
Le hook useReducer permet de gérer des états complexes en utilisant une fonction réductrice.
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
</div>
);
}
5. useCallback
Le hook useCallback permet de memoiser des fonctions afin d'éviter les re-rendus inutiles.
import React, { useState, useCallback } from 'react';
function InputField() {
const [value, setValue] = useState('');
const handleChange = useCallback((e) => {
setValue(e.target.value);
}, []);
return <input value={value} onChange={handleChange} />;
}
6. useMemo
Le hook useMemo permet de memoiser des valeurs afin d'éviter les recalculs inutiles.
import React, { useState, useMemo } from 'react';
function ExpensiveCalculation({ a, b }) {
const result = useMemo(() => {
// Simulate an expensive calculation
let sum = 0;
for (let i = 0; i < 1e6; i++) {
sum += a + b;
}
return sum;
}, [a, b]);
return <div>Result: {result}</div>;
}
Mise en pratique : projet fil rouge
Mini-projet : Gestionnaire de tâches
Voici un mini-projet complet qui utilise les hooks essentiels pour créer un gestionnaire de tâches.
npx create-react-app todo-list
cd todo-list
npm install react-router-dom
Étape 1 : Création des composants
src/App.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import TaskList from './TaskList';
import AddTask from './AddTask';
function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={TaskList} />
<Route path="/add" component={AddTask} />
</Switch>
</Router>
);
}
export default App;
src/TaskList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function TaskList() {
const [tasks, setTasks] = useState([]);
useEffect(() => {
fetchTasks();
}, []);
const fetchTasks = async () => {
try {
const response = await axios.get('https://api.example.com/tasks');
setTasks(response.data);
} catch (error) {
console.error(error);
}
};
return (
<div>
<h1>Task List</h1>
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title}</li>
))}
</ul>
</div>
);
}
export default TaskList;
src/AddTask.js
import React, { useState } from 'react';
import axios from 'axios';
function AddTask() {
const [title, setTitle] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.post('https://api.example.com/tasks', { title });
alert('Task added!');
setTitle('');
} catch (error) {
console.error(error);
alert('Error adding task');
}
};
return (
<div>
<h1>Add Task</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Task title"
/>
<button type="submit">Add</button>
</form>
</div>
);
}
export default AddTask;
Étape 2 : Configuration du projet
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
Erreurs frequentes et debugging
Erreur 1 : TypeError: Cannot read property 'map' of undefined
Code incorrect
const tasks = data.map(task => (
<li key={task.id}>{task.title}</li>
));
Code correct
const tasks = Array.isArray(data) ? data.map(task => (
<li key={task.id}>{task.title}</li>
)) : [];
Erreur 2 : Uncaught TypeError: Cannot read property 'length' of undefined
Code incorrect
const result = useMemo(() => {
let sum = 0;
for (let i = 0; i < 1e6; i++) {
sum += a + b;
}
return sum;
}, [a]);
Code correct
const result = useMemo(() => {
if (a !== undefined && b !== undefined) {
let sum = 0;
for (let i = 0; i < 1e6; i++) {
sum += a + b;
}
return sum;
}
return 0;
}, [a, b]);
Erreur 3 : Uncaught TypeError: Cannot read property 'length' of null
Code incorrect
const handleChange = useCallback((e) => {
setValue(e.target.value);
}, []);
Code correct
const handleChange = useCallback((e) => {
if (e.target && e.target.value !== undefined) {
setValue(e.target.value);
}
}, []);
Pour aller plus loin
Custom hooks: En apprenant à créer vos propres hooks personnalisés, vous pouvez partager la logique réutilisable entre les composants.
useRef: Ce hook est utile pour accéder aux éléments du DOM et à des valeurs persistantes qui ne déclenchent pas de re-rendus.
useTransition: Ce hook permet d'optimiser les performances en différant certains mises à jour jusqu'à ce que le navigateur soit prêt pour traiter de nouvelles tâches.
Défi pratique : Créer un mini-gestionnaire de notes
Créez un mini-gestionnaire de notes qui permet d'ajouter, modifier et supprimer des notes. Utilisez les hooks useState et useEffect pour gérer le state local et les effets secondaires.