Comment fonctionnent les réducteurs Redux

Publié: 2022-03-10
Résumé rapide ↬ Si vous avez utilisé Redux à un moment donné lors du développement d'une application pour gérer state , vous aurez très certainement rencontré des réducteurs. Ce tutoriel expliquera le concept de réducteurs et comment ils fonctionnent spécifiquement dans Redux.

Dans ce didacticiel, nous allons apprendre le concept de réducteurs et leur fonctionnement, en particulier dans les applications React. Afin de comprendre et de mieux utiliser Redux, une solide compréhension des réducteurs est essentielle. Les réducteurs permettent de mettre à jour l'état d'une application à l'aide d'une action. Il fait partie intégrante de la bibliothèque Redux.

Ce didacticiel est destiné aux développeurs qui souhaitent en savoir plus sur les réducteurs Redux. Une compréhension de React et Redux serait bénéfique. À la fin du didacticiel, vous devriez avoir une meilleure compréhension du rôle que jouent les réducteurs dans Redux. Nous allons écrire des démos de code et une application pour mieux comprendre les réducteurs et comment cela affecte l'état d'une application.

Qu'est-ce qu'un réducteur

Un réducteur est une fonction pure qui prend l'état d'une application et d'une action comme arguments et renvoie un nouvel état. Par exemple, un réducteur d'authentification peut prendre un état initial d'une application sous la forme d'un objet vide et une action lui indiquant qu'un utilisateur s'est connecté et a renvoyé un nouvel état d'application avec un utilisateur connecté.

Les fonctions pures sont des fonctions qui n'ont pas d'effets secondaires et renverront les mêmes résultats si les mêmes arguments sont passés.

Ci-dessous un exemple de fonction pure :

 const add = (x, y) => x + y; add(2, 5);

L'exemple ci-dessus renvoie une valeur basée sur les entrées, si vous passez 2 et 5 , vous obtiendrez toujours 7 , tant qu'il s'agit de la même entrée, rien d'autre n'affecte la sortie que vous obtenez, c'est un exemple de fonction pure.

Vous trouverez ci-dessous un exemple de fonction de réduction qui prend en compte un état et une action.

 const initialState = {}; const cartReducer = (state = initialState, action) => { // Do something here }

Définissons les deux paramètres qu'un réducteur prend, state et action .

Plus après saut! Continuez à lire ci-dessous ↓

État

Un état est les données avec lesquelles votre ou vos composants travaillent - il contient les données dont un composant a besoin et il dicte ce qu'un composant rend. Une fois qu'un objet d' state change, le composant effectue un nouveau rendu. Si un état d'application est géré par Redux, le réducteur est l'endroit où les changements d'état se produisent.

action

Une action est un objet qui contient la charge utile d'informations. Ils sont la seule source d'informations pour que le magasin Redux soit mis à jour. Les réducteurs mettent à jour le magasin en fonction de la valeur de action.type . Ici, nous définirons l' action.type comme ADD_TO_CART .

Selon la documentation officielle de Redux, les actions sont les seules choses qui déclenchent des modifications dans une application Redux, elles contiennent la charge utile des modifications apportées à un magasin d'applications. Les actions sont des objets JavaScript qui indiquent à Redux le type d'action à effectuer, généralement elles sont définies comme des fonctions comme celle ci-dessous :

 const action = { type: 'ADD_TO_CART', payload: { product: 'margarine', quantity: 4 } }

Le code ci-dessus est une valeur de payload typique qui contient ce qu'un utilisateur envoie et qui sera utilisée pour mettre à jour l'état de l'application. Comme vous pouvez le voir ci-dessus, l'objet d'action contient le type d'action et un objet de charge utile qui seraient nécessaires pour que cette action particulière soit effectuée.

Mise à jour de l'état à l'aide de réducteurs

Pour montrer comment fonctionnent les réducteurs, regardons le compteur ci-dessous :

 const increaseAction = { type: 'INCREASE', }; const decreaseAction = { type: 'DECREASE' }; const countReducer = (state = 0, action) => { switch(action.type){ case INCREASE: return state + 1; case DECREASE : return state -1; default: return state; } };

Dans le code ci-dessus, increaseAction et decreaseAction sont des actions utilisées dans le réducteur pour déterminer à quoi l' state est mis à jour. Ensuite, nous avons une fonction de réduction appelée countReducer , qui prend en compte une action et un state initial dont la valeur est 0 . Si la valeur de action.type est INCREASE , nous retournons un nouvel état qui est incrémenté de 1, sinon si c'est DECREASE un nouvel état qui est décrémenté de 1 est retourné. Dans les cas où aucune de ces conditions n'est signifiée, nous retournons state .

Mise à jour de l'état à l'aide de réducteurs : l'opérateur de propagation

L'état ne peut pas être directement modifié, pour créer ou mettre à jour l'état, nous pouvons utiliser l'opérateur de propagation JavaScript pour nous assurer que nous ne modifions pas directement la valeur de l'état, mais plutôt pour renvoyer un nouvel objet contenant un état qui lui est transmis et la charge utile de l'utilisateur.

 const contactAction = { type: 'GET_CONTACT', payload: ['0801234567', '0901234567'] }; const initialState = { contacts: [], contact: {}, }; export default function (state = initialState, action) { switch (action.type) { case GET_CONTACTS: return { ...state, contacts: action.payload, }; default: return state; }

Dans le code ci-dessus, nous utilisons un opérateur de propagation pour nous assurer que nous ne modifions pas directement la valeur d'état, de cette façon nous pouvons renvoyer un nouvel objet qui est rempli avec l'état qui lui est transmis et la charge utile qui est envoyée par le utilisateur. En utilisant un opérateur de propagation, nous pouvons nous assurer que l'état reste le même lorsque nous y ajoutons tous les nouveaux éléments et remplaçons également le champ contacts dans l'état s'il était présent auparavant.

Réducteurs Redux en action - Une démo

Pour mieux comprendre les réducteurs Redux et leur fonctionnement, nous allons implémenter une simple application de recherche de détails de films, le code et la version de travail peuvent être trouvés ici sur Codesandbox. Pour commencer, accédez à votre terminal et initialisez une application de réaction à l'aide de la commande ci-dessous :

 create-react-app movie-detail-finder

Une fois notre projet initialisé, installons ensuite les packages dont nous aurions besoin pour notre application.

 npm i axios reactstrap react-redux redux redux-thunk

Une fois les packages installés, lançons notre serveur de développement à l'aide de la commande :

 npm start

La commande ci-dessus devrait démarrer notre serveur de développement de projet dans notre navigateur. Ouvrons ensuite notre projet dans l'éditeur de texte de notre choix, dans le dossier src de notre projet, supprimons les fichiers suivants : App.css , App.test.js , serviceWorker.js et setupTests.js . Ensuite, supprimons tout le code qui fait référence aux fichiers supprimés sur notre App.js .

Pour ce projet, nous utiliserons l'API Open Movie Database pour obtenir les informations, le contenu et les images de notre film pour notre application, voici un lien vers l'API, vous devez vous inscrire et obtenir des clés d'accès afin de l'utiliser pour cela application, Une fois que vous avez terminé, procédons à notre application en construisant des composants.

Créer des composants d'application

Tout d'abord, à l'intérieur de notre dossier src dans notre répertoire de projet, créez un dossier appelé composants et à l'intérieur du dossier, créons deux dossiers appelés Movie et Searchbar , notre composant devrait ressembler à l'image ci-dessous :

dossier des composants
Dossier Composants. ( Grand aperçu )

Construire un composant de film

Construisons le composant Movies , qui décrira la structure des détails du film que nous obtiendrons de notre API. Pour ce faire, dans le dossier Movies de notre composant, créez un nouveau fichier Movie.js , créez ensuite un composant basé sur une classe pour les résultats de l'API, faisons-le ci-dessous.

 import React, { Component } from 'react'; import { Card, CardImg, CardText, CardBody, ListGroup, ListGroupItem, Badge } from 'reactstrap'; import styles from './Movie.module.css'; class Movie extends Component{ render(){ if(this.props.movie){ return ( <div className={styles.Movie}> <h3 className="text-center my-4"> Movie Name: {this.props.movie.Title} </h3> <Card className="text-primary bg-dark"> <CardImg className={styles.Img} top src={this.props.movie.Poster} alt={this.props.movie.Title}/> <CardBody> <ListGroup className="bg-dark"> <ListGroupItem> <Badge color="primary">Actors:</Badge> {this.props.movie.Actors} </ListGroupItem> <ListGroupItem> <Badge color="primary">Genre:</Badge> {this.props.movie.Genre} </ListGroupItem> <ListGroupItem> <Badge color="primary">Year:</Badge> {this.props.movie.Year} </ListGroupItem> <ListGroupItem> <Badge color="primary">Writer(s):</Badge> {this.props.movie.Writer} </ListGroupItem> <ListGroupItem> <Badge color="primary">IMDB Rating:</Badge> {this.props.movie.imdbRating}/10 </ListGroupItem> </ListGroup> <CardText className="mt-3 text-white"> <Badge color="secondary">Plot:</Badge> {this.props.movie.Plot} </CardText> </CardBody> </Card> </div> ) } return null } } export default Movie;

Dans le code ci-dessus, Utilisation des composants du package reactstrap , vous pouvez consulter la documentation ici. Nous avons construit un composant de carte qui inclut le nom du film, l'image, le genre, l'acteur, l'année, l'auteur du film, le classement et l'intrigue. Pour faciliter la transmission des données à partir de ce composant, nous avons créé des données pour servir d'accessoires à d'autres composants. Ensuite, construisons notre composant Searchbar .

Construire notre composant de barre de recherche

Notre composant Searchbar comportera une barre de recherche et un composant de bouton pour rechercher des composants de film, faisons-le ci-dessous :

 import React from 'react'; import styles from './Searchbar.module.css'; import { connect } from 'react-redux'; import { fetchMovie } from '../../actions'; import Movie from '../Movie/Movie'; class Searchbar extends React.Component{ render(){ return( <div className={styles.Form}> <div> <form onSubmit={this.formHandler}> <input type="text" placeholder="Movie Title" onChange={e => this.setState({title: e.target.value})} value={this.state.title}/> <button type="submit">Search</button> </form> </div> <Movie movie={this.props.movie}/> </div> ) } }

Dans le code ci-dessus, nous importons connect from react-redux qui est utilisé pour connecter un composant React au magasin Redux, fournit au composant des informations du magasin et fournit également des fonctions utilisées pour envoyer des actions au magasin. Ensuite, nous avons importé le composant Movie et une fonction fetchMovie à partir des actions.

Ensuite, nous avons une balise de formulaire avec une zone de saisie pour entrer nos titres de films, en utilisant le crochet setState de React, nous avons ajouté un événement onChange et une valeur qui définira l'état du title sur la valeur entrée dans la zone de saisie. Nous avons une balise de button pour rechercher des titres de films et en utilisant le composant Movie que nous avons importé, nous avons transmis les propriétés du composant en tant props au résultat de la recherche.

La prochaine étape consiste à écrire une fonction pour soumettre le titre de notre film à l'API afin de nous envoyer les résultats. Nous devons également définir l'état initial de l'application. faisons cela ci-dessous.

 class Searchbar extends React.Component{ state = { title: '' } formHandler = (event) => { event.preventDefault(); this.props.fetchMovie(this.state.title); this.setState({title: ''}); }

Ici, nous définissons l'état initial de l'application sur des chaînes vides, nous avons créé une fonction formHandler qui prend un paramètre d'événement et passe la fonction fetchMovie de l'action et définit le titre comme nouvel état de l'application. Pour compléter notre application, exportons ce composant en utilisant la propriété connect de react-redux , pour ce faire, nous utiliserons la propriété react redux mapToStateProps pour sélectionner la partie des données dont notre composant aurait besoin, vous pouvez en savoir plus sur mapToStateProps ici.

 const mapStateToProps = (state) => { return { movie: state.movie } } export default connect(mapStateToProps, { fetchMovie })(Searchbar)

Ajoutons des styles à notre formulaire en créant un fichier Searchbar.module.css et en ajoutant les styles ci-dessous :

 .Form{ margin: 3rem auto; width: 80%; height: 100%; } input{ display: block; height: 45px; border: none; width: 100%; border-radius: 0.5rem; outline: none; padding: 0 1rem; } input:focus, select:focus{ border: 2px rgb(16, 204, 179) solid; } .Form button{ display: block; background: rgb(16, 204, 179); padding: 0.7rem; border-radius: 0.5rem; width: 20%; margin-top: 0.7rem; color: #FFF; border: none; text-decoration: none; transition: all 0.5s; } button:hover{ opacity: 0.6; } @media(max-width: 700px){ input{ height: 40px; padding: 0 1rem; } .Form button{ width: 40%; padding: 0.6rem; } }

Une fois que nous avons fait ce qui précède, notre composant de barre de recherche devrait ressembler à l'image ci-dessous :

Composant barre de recherche
Composant barre de recherche. ( Grand aperçu )

Création d'actions pour l'application

Dans ce composant, nous allons configurer des actions Redux pour notre application. Tout d'abord, dans le répertoire src , créez un dossier nommé actions et dans le dossier, nous créerons un fichier index.js . Ici, nous créerions une fonction fetchMovie qui prend un paramètre de titre et récupère le film à partir de l'API à l'aide d'Axios. Faisons cela ci-dessous :

 import axios from 'axios'; export const fetchMovie = (title) => async (dispatch) => { const response = await axios.get( `https://cors-anywhere.herokuapp.com/https://www.omdbapi.com/?t=${title}&apikey=APIKEY`); dispatch({ type: 'FETCH_MOVIE', payload: response.data }) }

Dans le code ci-dessus, nous avons importé axios et créé une fonction appelée fetchMovie qui prend un paramètre de title en utilisant async/wait afin que nous puissions faire une demande au serveur API. Nous avons une fonction de dispatch qui distribue au Redux l'objet action qui lui est passé. D'après ce que nous avons ci-dessus, nous envoyons une action avec le type FETCH_MOVIE et la charge utile qui contient la réponse que nous avons obtenue de l'API.

REMARQUE : L' apikey dans la demande sera remplacée par votre propre apikey après l'inscription à OmdbAPI .

Création de réducteurs d'application

Dans cette section, nous allons créer des réducteurs pour notre application.

 const fetchMovieReducer = (state = null, action) => { switch(action.type){ case 'FETCH_MOVIE': return action.payload; default: return state; } } const rootReducer = (state, action) => { return { movie: fetchMovieReducer(state, action) } } export default rootReducer;

Dans le code ci-dessus, nous avons créé un fetchMovieReducer qui prend un état par défaut de null et un paramètre d' action , en utilisant un opérateur de commutation, pour le cas FETCH_MOVIE , nous renverrons la valeur de l' action.payload qui est le film que nous avons obtenu de l'API. Si l'action que nous avons essayé d'effectuer n'est pas dans le réducteur, nous renvoyons notre état par défaut.

Ensuite, nous avons créé une fonction rootReducer qui accepte l'état actuel et une action en entrée et renvoie le fetchMovieReducer .

Mettre ensemble

Dans cette section, nous terminerions notre application en créant notre magasin redux dans le index.js , faisons-le ci-dessous :

 import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import App from './App'; import 'bootstrap/dist/css/bootstrap.min.css'; import './index.css'; import reducers from './reducers'; const store = createStore(reducers, applyMiddleware(thunk)) ReactDOM.render( <Provider store={store}> <> <App/> </> </Provider>, document.getElementById('root') )

Dans le code ci-dessus, nous avons créé le store d'applications à l'aide de la méthode createStore en passant le réducteur que nous avons créé et un middleware. Les middlewares sont des addons qui nous permettent d'améliorer les fonctionnalités de Redux. Ici, nous utilisons le middleware Redux Thunk en utilisant applyMiddleware . Le middleware Redux Thunk est nécessaire pour que notre magasin effectue des mises à jour asynchrones. Cela est nécessaire car, par défaut, Redux met à jour le magasin de manière synchrone.

Pour nous assurer que notre application connaît le magasin exact à utiliser, nous avons enveloppé notre application dans un composant Provider et passé le magasin en tant qu'accessoire, ce faisant, d'autres composants de notre application peuvent se connecter et partager des informations avec le magasin.

Ajoutons un peu de style à notre fichier index.css .

 *{ margin: 0; padding: 0; box-sizing: border-box; } body{ background: rgb(15, 10, 34); color: #FFF; height: 100vh; max-width: 100%; }

Rendu et test d'un outil de recherche de détails de film

Dans cette section, nous allons conclure notre application en rendant notre application dans notre App.js , pour ce faire, créons un composant basé sur une classe nommé App et initialisons notre barre de recherche et notre champ de saisie.

 import React from 'react'; import Searchbar from './components/Searchbar/Searchbar'; import styles from './App.module.css'; class App extends React.Component{ render(){ return( <div className={styles.App}> <h1 className={styles.Title}>Movies Search App</h1> <Searchbar/> </div> ) } } export default App;

Ici, nous avons créé un composant basé sur la classe App avec un h1 qui indique Movie Search App et ajouté notre composant Searchbar . Notre application devrait ressembler à l'image ci-dessous :

application de détails de film avec des réducteurs
Application finale des détails du film à l'aide de réducteurs. ( Grand aperçu )

Une démo fonctionnelle est disponible sur Codesandbox.

Conclusion

Les réducteurs sont une partie importante de la gestion de l'état Redux, avec des réducteurs, nous pouvons écrire des fonctions pures pour mettre à jour des zones spécifiques de nos applications Redux sans effets secondaires. Nous avons appris les bases des réducteurs Redux, leurs utilisations et le concept de base des réducteurs, de l'état et des arguments.

Vous pouvez aller plus loin en consultant la documentation sur les réducteurs Redux ici. Vous pouvez aller plus loin et construire davantage sur les réducteurs Redux, faites-moi savoir ce que vous construisez.

Ressources

  • Documentation React-Redux
  • Documentation redux
  • fonction connect()
  • fonction applyMiddleware