• 15 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 01/12/2023

Adaptez vos actions pour manipuler des événements asynchrones

Vous en avez marre de passer votre temps à cliquer sur les boutons “Point Joueur 1” et “Point Joueur 2” ? Moi aussi !

Dans ce chapitre, nous allons mettre en place un bouton qui jouera un point de manière automatique. Il attribuera un point aléatoire après 2 secondes d’attente (pour le suspense).

Découvrez comment Redux interagit avec l’asynchrone

Pour attendre 2 secondes, nous allons devoir utiliser setTimeout  qui est une fonction asynchrone. Redux ne peut pas manipuler du code asynchrone directement, en revanche on peut interagir avec Redux depuis une fonction asynchrone :

  • la fonction getState  pour lire le state actuel de Redux ;

  • dispatch  pour envoyer une action et mettre à jour le state.

Au fil du temps, les fonctions dispatch et getState permettent d’interagir avec Redux.
Processus asynchrone et interactions avec Redux

Dans le cas de Tennis Score, nous allons utiliser :

  • la fonction getState  de Redux pour savoir si le jeu est en train d'être joué ou s'il est terminé ; 

  • et dispatch  pour changer la propriété playing  et lorsqu’un joueur marque un point.

Pour pouvoir utiliser getState  et dispatch  , nous allons avoir besoin d'accéder au store  . Nous allons donc utiliser le hook useStore  de React-Redux.

import { useStore } from "react-redux";
 
export function MyComponent() {
    const store = useStore();
    // ...
}

Pourquoi on n’importe pas directement le store depuis le fichier store.js  ?

Cela serait effectivement possible, cependant utiliser les hooks de React-Redux permet de facilement changer le store utilisé via le Provider. C’est particulièrement utile pour tester nos composants, mais nous verrons cela dans un prochain chapitre.

Faites des appels asynchrones dans un composant

Il est temps de mettre en place la logique asynchrone. On commence par changer la valeur par défaut du state playing  dans le fichier src/store.js  . Nous allons l’utiliser pour savoir si l'action asynchrone est en cours ou non.

const initialState = {
    player1: 0,
    player2: 0,
    advantage: null,
    winner: null,
    // le jeu est en pause à l'initialisation
    playing: false,
    history: []
};

On va aussi transformer l’action playPause  en setPlaying  pour pouvoir passer la nouvelle valeur de playing  directement en playload :

export const setPlaying = (playing) => ({
    type: "setPlaying",
    payload: playing,
});
 
// dans le reducer
if (action.type === "setPlaying") {
    return produce(state, (draft) => {
        draft.playing = action.payload;
    });
}

Nous allons utiliser le composant existant PlayPauseButton  pour lancer l’action asynchrone au clic sur le bouton :

import { useStore } from "react-redux";
import { playPauseAction, pointScored } from "./actions";
 
export function PlayPauseButton() {
    const store = useStore();
 
    return (
        <button
            className="button"
            onClick={() => {
                const isPlaying = store.getState().playing;
                if (isPlaying) {
                    // Déjà entrain de jouer, on ne fait rien
                    return;
                }
                // on indique que la partie est en cours
                store.dispatch(setPlaying(true));
                // on utilise setTimeout pour attendre 2 secondes
                window.setTimeout(() => {
                    // le jeu est-il toujours en cours ?
                    if (store.getState().playing === false) {
                        // Si non, on ne fait rien
                        return;
                    }
                    // si oui on marque un point aléatoire
                    const pointWinner = Math.random() > 0.5 ? "player1" : "player2";
                    store.dispatch(pointScored(pointWinner));
                    // on remet le jeu en pause
                    store.dispatch(setPlaying(false));
                }, 2000);
            }}
        >
            Pause / Reprendre
        </button>
    );
}

Pourquoi on vérifie deux fois si le jeu est en pause ?

La première vérification est là pour éviter que l’action soit lancée plusieurs fois en simultané. Autrement dit, une fois que l’on clique sur le bouton, tant que les deux secondes ne sont pas passées, l’action n’est pas relancée.

La seconde vérification est là pour vérifier qu'après les deux secondes, le jeu est toujours en cours. En effet, une autre action peut avoir eu lieu entretemps, qui aurait stoppé le jeu. C’est par exemple le cas si l’on clique sur le bouton “Remettre à zéro”.

Isolez la logique asynchrone dans une fonction

Pour éviter d’avoir des composants trop compliqués, on peut extraire le code du onClick ci-dessus dans une fonction. C’est un peu le même principe que pour les selectors, sauf que cette fois on va mettre la fonction avec les actions, dans le fichier src/store.js  .

Pour accéder au store  (et donc à getState  et dispatch  ) dans cette fonction, il va falloir l’envoyer en paramètre depuis notre composant !

export function autoplay(store) {
  // ...
}

Voici maintenant la dernière étape dans le paramétrage de cette action automatique :

Vous pouvez retrouver le code de ce screencast sur la branche P2C4-autoplay du repository.

Exercez-vous

À vous de mettre en place la logique asynchrone dans Tennis Score. Vous allez devoir mettre en place une fonction qui toutes les 2 secondes attribue un point à un joueur aléatoirement, jusqu'à ce que le jeu soit gagné par un des joueurs.

N’oubliez pas de déclarer cette action asynchrone dans le fichier actions.js  !

Une fois que vous avez terminé, allez jeter un œil à la version corrigée sur la branche P2C4-solution du repository.

En résumé

  • Redux n’est pas capable de manipuler des événements asynchrones, mais on peut interagir avec dans une fonction asynchrone.

  • Dans les composants React, on peut utiliser le hook useStore  pour accéder au store et utiliser getState  et dispatch  dans notre action.

  • Il est recommandé de déclarer les actions asynchrones dans des fonctions séparées (dans le fichier store.js  , par exemple) ; il faut alors passer le store en paramètre.

Tennis Score nous a permis de mieux comprendre le fonctionnement de Redux et React-Redux, mais il est temps de passer à l’étape supérieure ! Suivez-moi dans la partie suivante pour découvrir toutes les astuces et méthodes pour mettre en place Redux dans un projet à grande échelle. 🚀

Exemple de certificat de réussite
Exemple de certificat de réussite