Module 3Gérer l'état avec Redux
Chapitre 16 sur 18
Déployer Redux dans un projet à grande échelle
Redux DevTools, les middlewares, les fonctions thunk avec Redux Thunk, et le chargement des posts avec axios.
- ≈ 95 min
- 3 leçons
- 4 vidéos
- 2 exercices
Module 3Gérer l'état avec Redux
Chapitre 16 sur 18
Redux DevTools, les middlewares, les fonctions thunk avec Redux Thunk, et le chargement des posts avec axios.
Objectifs
Avec ce chapitre, tu sauras…
loading, data, error).Leçon 1
Les Redux DevTools permettent d'explorer le state et les actions envoyées en temps réel, mais aussi de revenir en arrière pour comprendre l'évolution du state pas à pas.
L'installation
import {legacy_createStore as createStore} from 'redux'
const store=createStore(reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)window.__REDUX_DEVTOOLS_EXTENSION__ && … : si l'extension est installée, on l'active ; sinon l'expression vaut undefined et l'application fonctionne normalement.
L'exploration (projet projet-dev-tools)
Décompresse le projet fourni, installe les dépendances (npm install), lance-le (npm start), puis clic droit sur la page → Redux DevTools → To right (ou onglet Redux des outils de développement, F12).
| Zone / volet | Ce qu'on y voit |
|---|---|
| la liste à gauche | toutes les actions envoyées, dans l'ordre |
| Action | le détail de l'action sélectionnée (son type, son payload) |
| State | l'état après l'action, en Tree (arbre), Chart (graphique) ou Raw (texte JSON) |
| Diff | les valeurs du state avant et après l'action sélectionnée (ce qui a changé) |
| Log monitor | tous les états successifs de l'application |
Vidéo · À regarder
ENRedux and Modern Redux Toolkit with Asynchronous Operation – Full Course
freeCodeCamp.org · YouTube
Leçon 2
Vidéo · À regarder
ENRedux and Modern Redux Toolkit with Asynchronous Operation – Full Course
freeCodeCamp.org · YouTube
Leçon 3
| Écriture | Rôle |
|---|---|
applyMiddleware(thunk) | ajoute le middleware Thunk |
composeWithDevTools(applyMiddleware(thunk)) | ajoute en plus Redux DevTools |
createStore(reducer, composeWithDevTools(applyMiddleware(thunk))) | crée le store avec Thunk et DevTools |
1Redux DevTools permet…
2Le volet qui montre l'état avant et après une action est…
3Le bouton qui empêche de perdre les actions quand on actualise la page est…
4Une fonction thunk reçoit…
5Avec Redux Thunk, on peut passer à dispatch…
6applyMiddleware(thunk) sert à…
7Les trois actions d'une requête sont…
8Avec redux-thunk 3, on importe…
L'essentiel du chapitre
createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) ; volets Action, State (Tree, Chart, Raw), Diff, Log monitor ; outils Dispatch, Slider, Persist, Lock.dispatch et le reducer ; Redux Thunk permet de dispatcher une fonction (dispatch, getState) => { … }.loading: true), SUCCESS (data), FAILURE (error).createStore(reducer, composeWithDevTools(applyMiddleware(thunk))).import { thunk } from "redux-thunk", @redux-devtools/extension, ou directement configureStore de Redux Toolkit.Dans le support
Vidéo · À regarder
ENRedux and Modern Redux Toolkit with Asynchronous Operation – Full Course
freeCodeCamp.org · YouTube
Vidéo · À regarder
ENRedux and Modern Redux Toolkit with Asynchronous Operation – Full Course
freeCodeCamp.org · YouTube
Les autres outils (barre du bas) :
{ type: "INCREMENTER" }) puis on clique sur Dispatch : on lance une action sans passer par l'interface ;Ce que tu vas voir (en anglais, sous-titres français) : le formateur installe l'extension Redux DevTools, branche le store, puis montre la liste des actions, les volets Action, State et Diff, et utilise le slider pour rejouer les changements. C'est la section 1.
Au chapitre 14, le flux Redux était synchrone : dispatch(action) → reducer → nouvel état. Avec de la logique asynchrone, on ajoute une étape : un middleware exécute la logique (par exemple une requête Ajax) puis envoie (dispatch) les actions.
@startuml
skinparam shadowing false
rectangle "UI\n(bouton « charger posts »)" as UI
rectangle "Middleware Thunk\n(fonction thunk)" as MW
rectangle "API\njsonplaceholder" as API
rectangle "Reducer" as R
rectangle "Store / State\nloading, data, error" as S
UI --> MW : dispatch(fetchPosts())
MW --> R : 1. dispatch(fetchPostsRequest())
MW --> API : 2. axios.get(…)
API --> MW : 3. réponse ou erreur
MW --> R : 4. dispatch(fetchPostsSuccess(data))\nou dispatch(fetchPostsFailure(err))
R --> S
S --> UI : useSelector → nouvel affichage
@endumlLa fonction thunk
Une fois le middleware thunk ajouté au store, on peut passer des fonctions (et pas seulement des objets) à store.dispatch. Une fonction thunk est toujours appelée avec (dispatch, getState) ; on peut les utiliser à l'intérieur si besoin. Elle envoie en général des actions simples créées par des créateurs d'action.
//fonction thunk fetchPosts
export const fetchPosts=function(){
return function(dispatch,getState){
dispatch(fetchPostsRequest())
axios.get('https://jsonplaceholder.typicode.com/posts').
then(response=> dispatch(fetchPostsSuccess(response.data))).
catch(err=>dispatch(fetchPostsFailure(err.message)))
}
}fetchPosts() renvoie une fonction qui reçoit dispatch et getState ;store.dispatch et store.getState ;fetchPosts envoie les actions simples : fetchPostsRequest puis fetchPostsSuccess (succès), ou fetchPostsRequest puis fetchPostsFailure (échec).Un middleware se place entre dispatch et le reducer. Il reçoit chaque action et peut la laisser passer, la modifier, la bloquer, écrire un journal, ou, comme Redux Thunk, exécuter l'action si c'est une fonction. On l'ajoute au store avec applyMiddleware(...).
Ce que tu vas voir (en anglais, sous-titres français) : ce qu'est un middleware dans Redux, puis le formateur ajoute le middleware redux-logger avec applyMiddleware(logger) : chaque action s'affiche dans la console avec l'état avant et après. C'est la définition du middleware ci-dessus.
Le bouton « charger posts » charge les posts de https://jsonplaceholder.typicode.com/posts avec Redux, axios et le middleware Thunk. Les dépendances :
npm install react-redux redux
npm install redux-thunk
npm install axios
npm install --save-dev redux-devtools-extensionLes actions
export const FETCH_POSTS_REQUEST='FETCH_POSTS_REQUEST';
export const FETCH_POSTS_SUCCESS='FETCH_POSTS_SUCCESS';
export const FETCH_POSTS_FAILURE='FETCH_POSTS_FAILURE';import * as type from './postsType'
import axios from 'axios'
//actions createurs
export const fetchPostsRequest=()=>{
return{
type:type.FETCH_POSTS_REQUEST
}
}
export const fetchPostsSuccess=(posts)=>{
return {
type:type.FETCH_POSTS_SUCCESS,
payload:{posts:posts}
}
}
export const fetchPostsFailure=(err)=>{
return{
type:type.FETCH_POSTS_FAILURE,
payload:{error:err}
}
}
//fonction thunk fetchPosts
export const fetchPosts=function(){
return function(dispatch,getState){
dispatch(fetchPostsRequest())
axios.get('https://jsonplaceholder.typicode.com/posts').
then(response=> dispatch(fetchPostsSuccess(response.data))).
catch(err=>dispatch(fetchPostsFailure(err.message)))
}
}Trois actions décrivent les trois moments d'une requête : REQUEST (elle part), SUCCESS (les données sont arrivées), FAILURE (erreur).
Le store
import {applyMiddleware} from 'redux'
import thunk from "redux-thunk";
import {composeWithDevTools } from 'redux-devtools-extension'
import { legacy_createStore as createStore } from 'redux';
import * as type from './postsType'
const initialState={
loading:false,
data:[],
error:''
}
const reducer=(state=initialState,action)=>{
switch(action.type){
case type.FETCH_POSTS_REQUEST:
return {loading:true,data:[],error:''}
case type.FETCH_POSTS_SUCCESS:
return{ loading:false,
data:action.payload.posts,
error:''}
case type.FETCH_POSTS_FAILURE:
return {loading:false,
data:[],
error:action.payload.error}
default:
return state
}}
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)));import { thunk } from "redux-thunk" (avec accolades).@redux-devtools/extension : npm install @redux-devtools/extension puis import { composeWithDevTools } from "@redux-devtools/extension".configureStore ajoute automatiquement Thunk et DevTools : aucune de ces lignes n'est nécessaire.import ReactDOM from 'react-dom/client';
import App from './components/App';
import store from './components/store';
import {Provider} from 'react-redux';
const root=ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store={store}>
<App/>
</Provider>)Les composants Posts et App
import {useSelector} from 'react-redux'
export default function Posts(){
const posts=useSelector(state=>state.data)
return(<ul>
{posts.map((post,index)=><li key={index}>{post.id}:{post.title}</li>)}
</ul>)
}import {useSelector,useDispatch} from 'react-redux'
import Posts from './Posts';
import { fetchPosts } from './postsActions'
export default function App(){
const posts=useSelector(state=>state)
const dispatch=useDispatch();
function charger(){
dispatch(fetchPosts())
}
return(<>
<button onClick={()=>charger()}>charger posts</button>
{posts.loading && <h1>chargement</h1>}
{(!posts.loading && !posts.error) && <div><h3>nombre de posts {posts.data.length}</h3>
<Posts/>
</div>}
{(!posts.loading && posts.error) && <h3>erreur {posts.error}</h3>}
</>)
}Ce qui se passe au clic :
dispatch(fetchPosts()) : fetchPosts() renvoie une fonction ; Thunk l'exécute ;loading: true → « chargement » s'affiche ;data contient les 100 posts → « nombre de posts 100 » et la liste ;Dans Redux DevTools, tu vois passer les deux actions FETCH_POSTS_REQUEST puis FETCH_POSTS_SUCCESS.
En utilisant Redux, Thunk et axios, crée l'application qui affiche la liste des users de l'endpoint https://jsonplaceholder.typicode.com/users (nom, email, ville), avec le message de chargement et l'erreur en rouge. Vérifie les actions dans Redux DevTools.
Écris le middleware logger qui affiche dans la console le type de chaque action, l'état avant et l'état après : const logger = store => next => action => { … }. Ajoute-le au store avec applyMiddleware(thunk, logger).
useSelector(state => state) renvoie tout l'état : le composant se réaffiche à chaque changement du store. Ici l'état est petit, c'est acceptable ; dans une grande application, on sélectionne seulement ce qui est utile (state => state.loading, state => state.error…). Dans Posts, la clé devrait être post.id plutôt que l'index.
Ce que tu vas voir (en anglais, sous-titres français) : l'installation de redux-thunk et axios, le state { loading, users, error }, les trois créateurs d'action request / success / failure, puis la fonction thunk fetchUsers qui renvoie function (dispatch) { … }, ajoutée au store avec applyMiddleware(thunk). C'est le même code que fetchPosts.
Ce que tu vas voir (en anglais, sous-titres français) : la même requête dans une application React : la fonction thunk appelée avec dispatch depuis un composant (avec useDispatch et useSelector), l'affichage du message de chargement, de la liste ou de l'erreur. C'est App.js et Posts.js de cette section.