• 8 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 31/01/2024

Modifiez les valeurs du state de la bonne manière avec les Reducers

Rappelez-vous l’immutabilité

Maintenant que nous avons mis en place une méthode qui permet à la fois de centraliser un state et de communiquer les changements de celui-ci, nous allons nous concentrer sur la manière dont nous modifions sa valeur.

Si nous nous souvenons du fonctionnement de JavaScript au niveau de l’assignation, c'est-à-dire l’association entre les variables et les valeurs, hormis les cas des primitives (“string”, “number”, “boolean”…), toutes les autres valeurs sont assignées par référence : c’est l’adresse mémoire où se trouve la valeur qui est stockée. La variable ne stocke pas directement la valeur !

const A = {name: 'joe'}
const B = {name: 'joe'}

A === B
// false

Et ceci parce que les valeurs de A et B n’ont pas la même référence. C'est-à-dire, elles ne sont pas placées au même endroit de la mémoire de votre ordinateur.

Comme nous l’avons vu dans la vidéo précédente, nous pouvons faire des copies par valeur pour nous assurer de l’immutabilité. C'est-à-dire que nous copions tout le contenu d’une variable et nous créons une nouvelle référence.

Par contre, si nous faisons :

const C = A
C.name = "claude"

A.name
// claude

A et C partagent la même référence.

D’accord, il y a les valeurs et les références, mais pourquoi s’arrêter là-dessus ici ?

Eh bien, si nous revenons sur notre implémentation précédente, nous mettons à jour la valeur de notre variable state à chaque dispatch  . Ceci peut présenter des effets non désirés et des incohérences dans les données. Selon le contexte dans lequel nous faisons appel à une variable, celle-ci ne retourne pas exactement le résultat attendu.

Je vous propose de suivre le screencast suivant où je vous montre comment ces effets peuvent avoir lieu :

Pour revenir à ce que nous venons de voir dans le screencast, copier une référence de state peut s’avérer contre-intuitif.

Dans notre exemple, nous avons appliqué une promotion aux produits du panier du type Super Crémeux, sauf que nous avons modifié la valeur price  de l’objet en référence SuperCremeux  . Donc à chaque nouvel ajout du produit  SuperCremeux  , celui-ci n’est plus au prix de base mais au prix promotionnel.

Pour éviter cette situation, nous allons créer une copie complète de notre state, et nous assurer qu’à chaque changement, ce soit la nouvelle copie modifiée qui soit transmise dans le dispatch.

Maîtrisez les changements avec les Reducers

Il faudra donc à chaque fois reconstruire un nouveau state ?

Oui, mais nous allons le faire de la bonne façon. Pour nous rapprocher de Redux nous allons simplifier notre méthode en introduisant un nouvel outil : le reducer.

Un reducer est une fonction qui prend le state courant et les données que nous souhaitons modifier et retourne le nouveau state.

const reducer = (currentState, dataToUpdate) => {
    const newState = {...currentState, …dataToUpdate}
    return newState
}

Plutôt simple comme implémentation ! Mais pas vraiment.

Si maintenant nos fonctionnalités se complètent avec la possibilité d’appliquer une promotion sur chaque produit SuperCremeux  et que l’on souhaite retirer un produit déjà sélectionné de la commande… On comprend vite que notre reducer risque de devenir compliqué !

C’est pourquoi les reducers avec Redux ont une implémentation un peu différente. Au lieu de passer uniquement des données à mettre à jour en paramètre, nous allons passer un objet contenant :

  • une instruction de ce que nous voulons effectuer ;

  • les données à modifier.

Cet objet, c’est ce qu’on appelle une action :

{
    type: 'APPLY_VOUCHER',
    payload: {
        price: 2,
    }
}

Dans le screencast suivant, je vous montre comment l’utiliser.

Nous avons dans ce screencast présenté comment appliquer une action dans notre reducer en veillant à bien recréer un nouveau state à chaque changement :

// Nous créons le reducer
const reducer = (currentState, action ) => {
    switch (action.type) {
        case 'ADD_PRODUCT':
            const listWithNewProduct = [...currentState.list, action.payload]
            return {...currentState, list: listWithNewProduct}
        case 'REMOVE_PRODUCT':
            const list = currentState.list.filter(
                (item, index) => index !== action.payload
            )
            return {...currentState, list: list}
        case 'APPLY_VOUCHER':
            const withVoucherList = currentState.list.map
                        item => item.title === 'Super Crémeux' ? ({...item, price: action.payload.price}) : item
            )
            return {...currentState, list: withVoucherList}

        case 'UPDATE_FIRSTNAME':
            const owner = {...currentState.owner, firstName: action.payload}
            return {...currentState, owner}
        default:
            return currentState
    }
}

// Et nous modifions en conséquence...
dispatch({...state, company: {name: 'Burger du Pré'}})
document.getElementById('addForm').addEventListener("submit", (evt) => {
    evt.preventDefault()
    const firstNameInput = evt.currentTarget.firstName
        const newState = reducer(state, {type: 'UPDATE_FIRSTNAME', payload: firstNameInput.value })
    dispatch(newState)
})

document.querySelectorAll('.orderButton').forEach((element) => {
    element.addEventListener('click', (event) => {
        const productId = event.target.dataset['id']
                const newState = reducer(state, {type: 'ADD_PRODUCT', payload: PRODUCT_LIST[productId] })
        dispatch(newState)
    })
})

document.getElementById('voucher').addEventListener("click", (evt) => {
    const newState = reducer(state, {type: 'APPLY_VOUCHER', payload: {price: 2.00} })
    dispatch(newState)
})

En résumé

  • L’immutabilité implique de ne pas modifier directement les valeurs d’un objet. Dans le cas du state, ce principe nous pousse à le remplacer par une copie modifiée.

  • Un reducer est une fonction qui prend le state et les données à modifier en paramètre pour modifier notre state.  

  • Une action est un objet comportant un type qui permet de définir quel traitement effectuer sur le state, et un payload, c'est-à-dire une valeur qui sera utilisée afin de modifier la valeur du state.

Maintenant, apprenons à utiliser Redux pour aller plus vite.

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