React Redux Avancé - Prise de notes

Présentation

Prise de notes réalisée pendant le suivi de la formation React v16 + Redux avancé.

Réalisé pendant la formation : sans relecture …

Introduction

  • Création du projet : npx create-react-app mon-app
  • Lancement : npm start

Nouveautés, Tips & Tricks

Les nouveautés cycle de vie de React

  • 3 fonctions supprimées : componentWillMount, componentWillReceiveProps, componentWillUpdate,
  • Remplacement :
    • componentWillMount : constructeur,
    • componentWillReceiveProps : static getDerivaedStateFromProps(props, state),
      • Si retourne null : RAS,
      • Si retourne qq chose = sera mis dans le state local du component,
      • En paramètre :
        • props: c’est bien les props à jour,
        • state: c’est le state courrant que l’on veut mettre à jour,
    • componentWillUpdate : static getSnapshitBeforeUpdate(prevProps, prevState),
      • Appelé APRES le render mais AVANT que le dom soit mis à jour,
      • Ce qui est retourné est passé en paramètre de la fonction componentDidUpdate,

RAPPEL : il faut les utiliser au minimum.

PureComponent

Un PureComponent peut-être vu comme un composant “optimisé” : il n’est rendu que si une propriété a effectivement été modifiée. L’objectif étant d’éviter des renders() si rien ne change.

Attention cependant : la comparaison utilise “shallow equality” qui est une comparaison de référence ou de surface. Donc finalement à utiliser qu’avec des props “simples” ou implémenter des comparaisons mais qui peuvent être plus couteuses que le rendu.

API Context

Très macro : permet de mettre à dispo des variables dans les composants enfants au travers de Provider (pour le composant racine) et des Consumer (pour les composants enfants).

Cela peut-être utile si un composant enfant profond ( 4ème niveau par exemple) a besoin d’une propriété connue par le parent. Sans cela (ou sans redux :)), il faut passer la props dans la hiérarchie ce que ui peut être lourd. Ici, l’élément va être passé au context et consommable par les enfants via un consumer.

NOTE : un conseil, il faut éviter d’utiliser API Context et Redux dans un même projet.

Hooks

Résumé des hooks : avoir accès à certaines fonctionnalités des components de type classe mais dans un component fonction

Etats

  • useState : permet de créer un état,
    • const [age, setAge] = useState(0);

Effets

  • useEffect : permet de mettre en place le cycle de vie. Cf. la doc : “pensez au Hook useEffect comme une combinaison de componentDidMount, componentDidUpdate & componentWillMount.
  • En argument, il est possible de lui indiquer dans une tableau, les propriétés à surveiller. useEffect ne sera executée que si une des propriétés changes,
    • astuce: si le tableau est vide, le useEffect ne sera appelée la première fois (= componentDidMount).

Rappel de précédente notes :

  • componentDidMount : il ne faut pas passer de valeur à observer mais un tableau vide. Il s’exécutera qu’une seule fois,
  • componentWillUnmount : il faut utiliser la fonction de retour pour effectuer le nettoyage,
  • componentDidUpdate : simplement mettre en place la fonction.

React Router

  • Intro :
    • Toutes les actions sont passées directement …
    • L’ordre des import est important : d’abord les imports puis les requires,
  • Différence entre Router V3 et RouterV4 (macro) :
    • V3 : tout est centralisé dans un seul component,
    • V4 : chaque component gère ses routes.
  • Compléments :
    • Par défaut, il rend chaque composant qui matche l’URL donc potentiellement plusieurs,
      • Solution 1 : mettre exact pour le forcer à ce que la route soit exacte,
      • Solution 2 : mettre swicth pour qu’il n’affiche qu’un seul component,
      • Conseil : mettre les deux,
    • Récupération d’un paramètre : {this.props.match.params.[nonparam]}

HOC

  • HOC : High Order Component : Composant de plus haut niveau,
  • Important : c’est le HOC qui va “renderer” le composant,
  • Mais il va l’encapsuler permettant d’augmenter les capacités du composant de base mais aussi de centraliser des comportements,

MiddleWare

  • S’intercale entre les reducers et le dispatch ==> Implique que pour chaque action, le middleware est appelée
  • Pour enregistrer, il faut l’ajouter dans lors de l’appel de la création du store : const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
    • Qui pour le coup utilise déjà applyMiddleware,
    • Les middleware sont traités dans l’ordre,
  • Au départ, c’est une fonction qui doit retourner une fonction qui doit retourner une fonction qui doit retourner une fonction :
    • La première fonction reçoit le store dans son intégralité,
    • La deuxième fonction reçoit en paramètre un callback qui doit être la fonction qui doit être appelée quand c’est terminé,
    • La troisième fonction reçoit l’action qui vient d’être dispatché mais pas encore traiter par les reducers,
export const actionCounter = function(store) {
    return function(next) {
        return function(action) {
            next(action);
        };
    };
};
// Une autre syntaxe : plus simple ?
export const actionCounter = store => next => action {
    next(action);
}

Test

Mise en place d’Enzyme

// setup file
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";

configure({ adapter: new Adapter() });

Quelques fonctions

  • shallow: permet de créer un wrapper autour d’un composant permettant ensuite d’intéragir avec lui et faciliter les tests :
// Création
const wrapper = shallow(<TodoApp />);
// Recherche
expect(wrapper.find("div.list-group-item").length).toEqual(0);
// Simuler un changement dans un input
wrapper.find("input").simulate("change", {
    target: { value: "Yo" }
});
// Simulation d'un click
wrapper.find("#addButton").simulate("click");
// Validation
expect(wrapper.find("div.list-group-item").length).toEqual(1);
expect(wrapper.find("div.list-group-item").text()).toContain("Yo");
  • mount : permet de gérer les cycles de vie,

Tests connectés

Sur le principe, il faut pas faire cela mais si on souhaite testé un component connecté, il faut recréer toute la chaîne intégrant le Provider de redux :

const wrapper = mount(
    <Provider store={createStoreWithMiddleware(reducers)}>
        <MemoryRouter>
            <Header />
        </MemoryRouter>
    </Provider>
);
console.log(wrapper.debug());

Selecteur

Présentation

  • Un selecteur est une fonction qui reçoit le store en paramètre et là : elle fait ce qu’elle veut,
  • L’idée étant de metttre cela dans un mapStateToProps afin de centraliser un filtrage,
const mapStateToProps = store => {
    return {
        integerRessources: getIntergerList(store),
        isPairs: getPairs(store),
        containsOneRessources: getContains1List(store),
        primeNumbers: getPrimerNumberList(store)
    };
};

Reselect

  • https://github.com/reduxjs/reselect,
  • Librairie permet de créer de selecteur avec combinaison de fonctions, surveillance d’état, etc…
  • Fonction principal : createSelector,
    • reçoit le store en paramètre dynamiquement,
    • le passe à tous les selectors en paramètre,
    • finalement, appelle une fonction qui prend en paramètre tous les résultats.
export const getSpecialNumberList = createSelector(
    getContains1List,
    getPrimerNumberList,
    (containsOneList, primeNumbersList) => {
        return lodash.intersection(containsOneList, primeNumbersList);
    }
);

Dans l’exemple :

  • deux selectors sont passés en paramètre (getContainsList & getPrimeNumberList) (la liste pourrait être infinie),
  • le résultat est passé à la dernière fonction dans l’ordre des appels des selectors.

Normalement, la fonction est appelée que si un élement entrant change. Donc mon cas : appelé tous les temps ….

Mutation de state

C’est MAL !!!

En gros, au lieu de retourner un nouvel objet dans un reducer, celui-ci modifie directement le state. Et c’est MAL car ca peut tout péter.

Un moyen de se protéger : https://github.com/leoasis/redux-immutable-state-invariant.

Par contre … dans le doc, c’est marqué de l’utiliser uniquement en dev …