Cum funcționează reductoarele Redux
Publicat: 2022-03-10state
, 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
.
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:
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:
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:
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