React Redux Debutant - Prise de notes

Présentation

Cette prise de notes a été réalisé pendant le suivi de la formation React JS + Redux pour débutants.

Réalisée pendant la formation : sans relecture …

Projet 1 - Netflix des Bandes Annonces

  • Comment choisir ses composants :

    • Pas de règles, juste du bon sens et des questions,
    • Est-ce que le composant est réutilisable ? Centralisation, Responsabilité, …
    • Trouver le juste milieu,
  • Rappel :

    • chaque fois que l’on met à jour setState, render est appelé,
    • setStateest assynchrone,
    • Quand on appel un event, le scope this change. Il faut donc un bind (this.handleChange = this.handleChange.bind(this);),
    • Les props sont en lecture seule,
  • Cycle de vie :

    • Mounting (Création) :
      • constructor: récupération des props et gestion du state,
      • componentWillMount: appel des services API, mise en place des données, etc…
      • render : génération du rendu,
      • componentDidMount: quand c’est fini,
    • Update :
      • componentWillReceiveProps : Avant que les props soient mis à jour,
      • shoudlComponentUpdate: permet de valider que le component doit effectivement se mettre à jour,
      • componentWillUpdate: le component va se mettre à jour,
      • render,
      • componentDidUpdate: le component a été mis à jour,
    • Suppression :
      • componentWillUnmount: le composant va être “démonté” !,
      • componentDidUnmount: le composant a été démonté.
  • Axios :

    • npm install axios,

Projet 2 - Intro Redux - Consultation d’utilosateurs

  • Reducers :

    • Store : contient l’état (ou les états de l’application),
    • Reducers : fonction dont le résultat alimente le store,
    • Root Reducers : effectue le mappage entre le store et les différents reducers,
      • IMPORTANT : TOUS les reducers sont appelés !
  • Container : les composants qui doivent connaître les changements de morceaux de state,

  • mapStateToProps :

    • Tout ce qui est mis dans mapStateToProps est mis dans les props (le nom est clair mais pas obligatoire)
    • En fait, il faudrait l’appeller mapStoreToProps car c’est bien le store complet qui est passé,
  • Mise à jour state :

    • Création d’une action qui retourne le type et les données associées : le message,
    • Appeler cette fonction toute seule ne sert à rien car le composant n’a que faire que de l’action.
      • La cible de l’action c’est le reducer,
    • C’est le rôle de la fonction mapDispacthToProps (le nom est libre) :
      • Elle prend en paramètre la fonction dispatch qui est la fonction Redux qui va appeler tous les reducers en leur passant en paramètre le résultat d’un autre appel,
      • Dans cette fonction, le principe est de retourner un objet qui sera également “ajouter” aux props du composant,
        • Cette objet contient des fonctions qui retournent les actions qui doivent être passé à dispatch,
        • Un “raccourci” : utiliser bindActionCreators qui “simplifient” la syntaxe,
        • Bon en fait, c’est plus d’actualité …
    function mapDispatchToProps(dispatch) {
        // selectUser est importé depuis ../action/index.js et pointé par la proprité du même nom
        return bindActionCreators({selecteUser: selectUser}, dispatch);
        // est l'équivalent (enfin je crois de )
        return {
            selectUser: (user) => {
                dispatch(selectUser(user))
            }
        }
    }

Projet 3 - Taux de change

Redux Thunk :

  • https://github.com/reduxjs/redux-thunk,
  • Permet de gérer plusieurs dispatch sur une seule action, de gérer de l’asynchrone, …
  • Pour l’installer : npm install redux-thunk,
  • Pour le rendre dispo :
    • Classique : import thunk from "redux-thunk";,
    • Faut l’ajouter comme “middleware” : const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);,

Petit exo perso : que la liste des pays soient dynamique :

Créer une action spécifique :

export function getSupportedCurrencies() {
    return function(dispatch) {
        // La base USD est importante pour que les pays EUR soient aussi ramenés
        axios.get('https://api.exchangeratesapi.io/latest?base=USD')
            .then(axiosResponse => {
                dispatch({ type: GET_SUPPORTED_CURRENCIES, payload: axiosResponse.data.rates})
            });
    }
}

Créer le reducer associé :

import { GET_SUPPORTED_CURRENCIES } from "../actions";

const initialState = {
    supportedCurrencies: []
}

export default function(state = initialState, action) {

    switch (action.type) {
        case GET_SUPPORTED_CURRENCIES:
            return {
                ...state,
                supportedCurrencies: getCodes(action.payload)
            }
        default:
            return state;
    }

}

function getCodes(data) {
    return Object.keys(data).filter(c => c !== 'USD');
}

“Enregistrer” le reducer dans la liste :

import { combineReducers } from 'redux';
import ReducerCountries from "./reducer_countries";
import ReducerRateExchange from "./reducer_rate_exchange";
import ReducerSupportedCountries from "./reducer_supported_countries";

const rootReducer = combineReducers({
    // Le nouveay
    supportedCountriesReducer: ReducerSupportedCountries,
    countriesReducer: ReducerCountries,
    rateExchangeReducer: ReducerRateExchange
});

export default rootReducer;

Comme cela doit être chargé au “plus tôt”, j’ai mis cela dans le composant Apps,

    componentDidMount() {
        this.props.getSupportedCurrencies();
    }
    // [...]
    const mapDispatchToProps = {
    getSupportedCurrencies
    }


export default connect(null, mapDispatchToProps)(App);

Maintenant, il faut que les valeurs soient disponibles dans l’autre reducer ce qui n’est pas “prévu”,
Mais via redux-thunk et la méthode getState c’est possible (semble-t-il). Donc dans l’action de récupération des pays :

export function fecthCountries() {

    return function(dispatch, getState) { // Ajout de getState
        axios.get('https://restcountries.eu/rest/v2/all')
            .then((axiosResponse) => {
                const supportedCurrencies = getState().supportedCountriesReducer.supportedCurrencies // Passage d'une information supplémentaire
                dispatch({ type: GET_COUNTRIES, payload: axiosResponse.data, filter: supportedCurrencies })
            });
    }
}

Autres petits exos persos

  • Pouvoir supprimer un pays : une action, ajout d’un cas dans le reducer, mapDispatchToProps, onClick …
  • Un pays ne peut pas être ajouté deux fois,
  • Un sélecteur par défaut.

Partie 4 - Boite à Post

Mise en place

React-Router

  • https://reactrouter.com/,
  • npm install react-router,
  • Import : import { Router, Route, Link, browserHistory, IndexRoute } from "react-router";
    • Router : balise principale pour la gestion des routes,
      • Pour qu’elle puisse gérer l’history, il faut lui passer browserHistory sinon ca marche pas,
      • <Route path="/" component={PostList} />,
    • Route : permet de définir une route qui fait pointer un chemin (path) vers un component,
      • ex : <Route path="/" component={PostList} />,
      • Avec un paramètre :
        • <Route path="post/:id" component={Post} />,
        • Paramètre qui sont à disposition dans les props : this.props.params.id,
      • Gestion d’une route par défaut ou d’erreur : <Route path='*' component={NotFound} >.

React-Transition-Group

React Redux Form

  • https://redux-form.com,
  • En très gros : permet de lier le store à un formulaire (en très gros :)),
  • npm install redux-form,
  • Mise en place :
    • Import : import { reduxForm } from "redux-form";,
    • Il faut ensuite créer la configuration du formulaire :
      const formConfig = {
      form: 'createPostForm',                 // Nom du formulaire : obligatoire. Sert de clé pour le store
      fields: ['title', 'content', 'author'], // Les champs du formulaire
      validate: validate,                     // Pointeur de la fonction de validation
      initialValues: { author: 'Moi' }        // Gestion de valeurs initiales
      };
    • Il faut ensuite “connecter” la configuration et le composant : reduxForm(formConfig)(PostForm),
    • Puis, il faut utiliser la méthode handleSubmit de reduxform suite à la validation du formulaire : onSubmit={handleSubmit(this.createPost.bind(this))}
    • Pour que cela fonctionne, il faut également réserver une entrée dans le store :
      // [...]
      import { reducer as ReducerForm } from "redux-form";
      

const rootReducer = combineReducers({
posts: ReducerPosts,
activePost: ReducerActivePost,
form: ReducerForm
});