Cum funcționează reductoarele Redux

Publicat: 2022-03-10
Rezumat rapid ↬ Dacă ați folosit Redux în orice moment în timp ce dezvoltați o aplicație pentru gestionarea state , cu siguranță veți întâlni reductori. Acest tutorial va explica conceptul de reductoare și modul în care acestea funcționează în mod specific în Redux.

În acest tutorial, vom învăța conceptul de reductoare și modul în care funcționează, în special în aplicațiile React. Pentru a înțelege și a utiliza mai bine Redux, este esențială o înțelegere solidă a reductoarelor. Reductoarele oferă o modalitate de a actualiza starea unei aplicații folosind o acțiune. Este o parte integrantă a bibliotecii Redux.

Acest tutorial este pentru dezvoltatorii care doresc să afle mai multe despre Redux Reducers. O înțelegere a React și Redux ar fi benefică. La sfârșitul tutorialului, ar trebui să înțelegeți mai bine rolul pe care îl joacă reductorii în Redux. Vom scrie demonstrații de cod și o aplicație pentru a înțelege mai bine Reducerii și modul în care acestea afectează starea unei aplicații.

Ce este un reductor

Un Reductor este o funcție pură care ia ca argumente starea unei aplicații și a acțiunii și returnează o stare nouă. De exemplu, un reductor de autentificare poate lua o stare inițială a unei aplicații sub forma unui obiect gol și o acțiune care îi spune că un utilizator s-a conectat și a returnat o nouă stare de aplicație cu un utilizator conectat.

Funcțiile pure sunt funcții care nu au efecte secundare și vor returna aceleași rezultate dacă sunt transmise aceleași argumente.

Mai jos este un exemplu de funcție pură:

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

Exemplul de mai sus returnează o valoare bazată pe intrări, dacă treceți 2 și 5 , atunci veți obține întotdeauna 7 , atâta timp cât este aceeași intrare, nimic altceva nu afectează ieșirea pe care o obțineți, acesta este un exemplu de funcție pură.

Mai jos este un exemplu de funcție de reducere care ia o stare și o acțiune.

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

Să definim cei doi parametri pe care ia un reductor, state și action .

Mai multe după săritură! Continuați să citiți mai jos ↓

Stat

O stare este datele cu care lucrează componenta (componentele) dvs. - deține datele pe care le necesită o componentă și dictează ce redă o componentă. Odată ce un obiect de state se schimbă, componenta se redă din nou. Dacă starea unei aplicații este gestionată de Redux, atunci reductorul este locul în care au loc schimbările de stare.

Acțiune

O acțiune este un obiect care conține încărcătura utilă de informații. Sunt singura sursă de informații pentru ca magazinul Redux să fie actualizat. Reductorii actualizează magazinul în funcție de valoarea action.type . Aici vom defini action.type ca ADD_TO_CART .

Conform documentației oficiale Redux, acțiunile sunt singurele lucruri care declanșează modificări într-o aplicație Redux, ele conțin încărcătura utilă pentru modificări la un magazin de aplicații. Acțiunile sunt obiecte JavaScript care spun Redux tipul de acțiune care trebuie efectuată, de obicei sunt definite ca funcții precum cea de mai jos:

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

Codul de mai sus este o valoare tipică a payload care conține ceea ce trimite un utilizator și va fi folosit pentru a actualiza starea aplicației. După cum puteți vedea de mai sus, obiectul de acțiune conține tipul de acțiune și un obiect de sarcină utilă care ar fi necesare pentru ca această acțiune specială să fie efectuată.

Actualizarea stării utilizând reductoare

Pentru a arăta cum funcționează reductoarele, să ne uităm la contorul de numere de mai jos:

 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; } };

În codul de mai sus, increaseAction și decreaseAction sunt acțiuni utilizate în reductor pentru a determina la ce state este actualizată. În continuare, avem o funcție reducătoare numită countReducer , care ia o action și o state inițială a cărei valoare este 0 . Dacă valoarea action.type este INCREASE , returnăm o stare nouă care este incrementată cu 1, altfel, dacă este DECREASE , se returnează o stare nouă care este decrementată cu 1. În cazurile în care nu se menționează nici una dintre aceste condiții, returnăm state .

Actualizarea stării utilizând reductoare: operatorul Spread

Starea nu poate fi schimbată direct, pentru a crea sau actualiza starea, putem folosi operatorul de răspândire JavaScript pentru a ne asigura că nu schimbăm valoarea stării în mod direct, ci pentru a returna un nou obiect care conține o stare transmisă acestuia și sarcina utilă a utilizatorului.

 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; }

În codul de mai sus, folosim un operator de răspândire pentru a ne asigura că nu modificăm valoarea stării în mod direct, astfel putem returna un nou obiect care este umplut cu starea care îi este transmisă și sarcina utilă care este trimisă de către utilizator. Folosind un operator de răspândire, ne putem asigura că starea rămâne aceeași pe măsură ce îi adăugăm toate elementele noi și, de asemenea, înlocuim câmpul de contacte în starea dacă era prezent anterior.

Redux Reducers în acțiune — O demonstrație

Pentru a înțelege mai bine Redux Reducers și modul în care funcționează, vom implementa o aplicație simplă de căutare a detaliilor filmului, codul și versiunea de lucru pot fi găsite aici pe Codesandbox. Pentru a începe, accesați terminalul dvs. și inițializați o aplicație react folosind comanda de mai jos:

 create-react-app movie-detail-finder

Odată ce proiectul nostru s-a inițializat, apoi să instalăm pachetele de care avem nevoie pentru aplicația noastră.

 npm i axios reactstrap react-redux redux redux-thunk

Odată ce pachetele sunt instalate, să pornim serverul nostru de dezvoltare folosind comanda:

 npm start

Comanda de mai sus ar trebui să pornească serverul nostru de dezvoltare a proiectelor în browserul nostru. Apoi, să deschidem proiectul nostru în editorul nostru de text la alegere, în folderul nostru proiect src , ștergeți următoarele fișiere: App.css , App.test.js , serviceWorker.js și setupTests.js . Apoi, să ștergem tot codul care face referire la fișierele șterse din App.js

Pentru acest proiect, vom folosi Open Movie Database API pentru a obține informații despre film, conținut și imagini pentru aplicația noastră, aici este un link către API, va trebui să vă înregistrați și să obțineți cheile de acces pentru a-l utiliza pentru acest proiect. aplicație, după ce ați terminat, să continuăm cu aplicația noastră prin construirea componentelor.

Construirea componentelor aplicației

Mai întâi, în interiorul folderului nostru src din directorul nostru de proiect, creați un folder numit componente și în interiorul folderului, să creăm două foldere numite Movie și Searchbar , componenta noastră ar trebui să arate ca imaginea de mai jos:

folderul componente
Dosarul Componente. (Previzualizare mare)

Construirea componentei filmului

Să construim componenta Movies , care va sublinia structura detaliilor filmului pe care le vom obține din API-ul nostru. Pentru a face acest lucru, în folderul Movies al componentei noastre, creați un nou fișier Movie.js , apoi creați o componentă bazată pe clasă pentru rezultatele API, să facem asta mai jos.

 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;

În codul de mai sus, Folosind componente din pachetul reactstrap , puteți consulta documentația aici. Am construit o componentă Card care include numele filmului, imaginea, genul, actorul, anul, scriitorul de film, evaluarea și intriga. Pentru a facilita transmiterea datelor din această componentă, am construit datele pentru a fi elemente de recuzită către alte componente. În continuare, să construim componenta Searchbar de căutare.

Construirea componentei Barei de căutare

Componenta noastră Searchbar de căutare va include o bară de căutare și o componentă buton pentru căutarea componentelor filmului, să facem asta mai jos:

 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> ) } }

În codul de mai sus, importăm connect din react-redux care este folosit pentru a conecta o componentă React la magazinul Redux, furnizează componentului informații din magazin și oferă, de asemenea, funcții utilizate pentru a trimite acțiuni către magazin. Apoi, am importat componenta Movie și o funcție fetchMovie din acțiuni.

În continuare, avem o etichetă de formular cu o casetă de introducere pentru a introduce titlurile filmelor noastre, folosind setState hook de la React, am adăugat un eveniment și o valoare onChange care va seta starea title la valoarea introdusă în caseta de intrare. Avem o etichetă de button pentru a căuta titluri de filme și folosind componenta Movie pe care am importat-o, am trecut proprietățile componentei drept props rezultatului căutării.

Următorul pentru noi este să scriem o funcție pentru a trimite titlul filmului nostru la API pentru a ne trimite rezultate, trebuie să setăm și starea inițială a aplicației. hai sa facem asta mai jos.

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

Aici, am setat starea inițială a aplicației la șiruri goale, am creat o funcție formHandler care preia un parametru de eveniment și trece funcția fetchMovie din acțiune și setează titlul ca noua stare a aplicației. Pentru a finaliza aplicația noastră, să exportăm această componentă folosind proprietatea connect din react-redux , pentru a face acest lucru, am folosi proprietatea react redux mapToStateProps pentru a selecta partea de date de care ar avea nevoie componenta noastră, puteți afla mai multe despre mapToStateProps aici.

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

Să adăugăm stiluri în formularul nostru creând un fișier Searchbar.module.css și adăugând stilurile de mai jos:

 .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; } }

După ce am făcut cele de mai sus, componenta barei de căutare ar trebui să arate similar cu imaginea de mai jos:

Componenta barei de căutare
Componenta barei de căutare. (Previzualizare mare)

Crearea de acțiuni pentru aplicare

În această componentă, vom configura acțiuni Redux pentru aplicația noastră. Mai întâi, în directorul src , creați un folder numit actions și în interiorul folderului, vom crea un fișier index.js . Aici am crea o funcție fetchMovie care preia un parametru de titlu și preia filmul din API folosind Axios. Să facem asta mai jos:

 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 }) }

În codul de mai sus, am importat axios și am creat o funcție numită fetchMovie care preia un parametru de title folosind async/wait, astfel încât să putem face o cerere către serverul API. Avem o funcție de dispatch care trimite către Redux obiectul de acțiune care îi este transmis. Din ceea ce avem mai sus, trimitem o acțiune cu tipul FETCH_MOVIE și sarcina utilă care conține răspunsul primit de la API.

NOTĂ: apikey din cerere va fi înlocuit cu propriul apikey după înregistrarea la OmdbAPI .

Crearea de reduceri de aplicații

În această secțiune, vom crea reductoare pentru aplicația noastră.

 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;

În codul de mai sus, am creat un fetchMovieReducer care preia într-o stare implicită null și un parametru de action , folosind un operator de comutare, pentru cazul FETCH_MOVIE vom returna valoarea action.payload care este filmul pe care l-am primit din API. Dacă acțiunea pe care am încercat să o executăm nu se află în reductor, atunci ne întoarcem starea implicită.

Apoi, am creat o funcție rootReducer care va accepta starea curentă și o acțiune ca intrare și returnează fetchMovieReducer .

Punând laolaltă

În această secțiune, ne-am termina aplicația prin crearea magazinului nostru redux în index.js , să facem asta mai jos:

 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') )

În codul de mai sus, am creat store de aplicații folosind metoda createStore trecând reductorul creat de noi și un middleware. Middleware-urile sunt suplimente care ne permit să îmbunătățim funcționalitățile Redux. Aici folosim middleware-ul Redux Thunk folosind applyMiddleware . Middleware-ul Redux Thunk este necesar pentru ca magazinul nostru să facă actualizări asincrone. Acest lucru este necesar deoarece în mod implicit, Redux actualizează magazinul în mod sincron.

Pentru a ne asigura că aplicația noastră cunoaște exact magazinul de utilizat, am împachetat aplicația noastră într-o componentă Provider și am trecut magazinul ca o reclamă, făcând acest lucru, alte componente din aplicația noastră se pot conecta și partaja informații cu magazinul.

Să adăugăm un pic de stil fișierului nostru index.css .

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

Redarea și testarea unui instrument de căutare a detaliilor filmului

În această secțiune, vom încheia aplicația noastră prin redarea aplicației noastre în App.js , pentru a face acest lucru, să creăm o componentă bazată pe clasă numită App și să inițializam bara de căutare și câmpul de introducere.

 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;

Aici, am creat o componentă bazată pe clasă de aplicație cu un h1 care spune Movie Search App și am adăugat componenta noastră Searchbar . Aplicația noastră ar trebui să arate ca imaginea de mai jos:

aplicație detalii film cu reductoare
Aplicația finală a detaliilor filmului folosind reductoare. (Previzualizare mare)

O demonstrație funcțională este disponibilă pe Codesandbox.

Concluzie

Reductoarele sunt o parte importantă a managementului stării Redux, cu reductoare putem scrie funcții pure pentru a actualiza anumite zone ale aplicațiilor noastre Redux fără efecte secundare. Am învățat elementele de bază ale reductoarelor Redux, utilizările lor și conceptul de bază al reductorilor, starea și argumentele.

Puteți duce acest lucru mai departe, consultând documentația despre reductoarele Redux aici. Puteți duce acest lucru mai departe și puteți construi mai multe pe reductoarele Redux, spuneți-mi ce construiți.

Resurse

  • Documentația React-Redux
  • Documentație Redux
  • funcția connect() .
  • funcția applyMiddleware