Kuidas kasutada voogu oleku haldamiseks ReactJS-is - selgitatud näite abil

Kui olete hiljuti ReactJS-i kallal töötanud, võite mõelda, kuidas hallata Reactis olekut, et teie rakendus saaks laiendada

Selle riigi juhtimise küsimuse lahendamiseks on paljud ettevõtted ja inimesed välja töötanud erinevad lahendused. ReactJSi välja töötanud Facebook pakkus välja lahenduse nimega Flux .

Võib-olla olete kuulnud Redux kui olete töötanud esiotsa tehnoloogia nagu AngularJS või EmberJS . ReactJS-il on Reduxi juurutamiseks ka teek.

Kuid enne Reduxi õppimist soovitaksin teil Fluxist läbi minna ja sellest aru saada. Pärast seda proovige Reduxit. Ma ütlen seda seetõttu, et Redux on Fluxi täpsem versioon. Kui Fluxi mõisted on selged, saate reduxi õppida ja selle oma rakendusse integreerida.

Mis on voog?

Flux kasutab olekuhalduse keerukuse lahendamiseks ühesuunalist andmevoo mustrit . Pidage meeles, et see pole raamistik - pigem on see pigem muster, mille eesmärk on lahendada riigi juhtimise probleem.

Kas soovite teada, mis on olemasoleva MVC raamistikuga valesti? Kujutage ette, et teie kliendi rakendus suureneb. Teil on interaktsioon paljude mudelite ja vaadete vahel. Kuidas see välja näeks?

Komponentide suhe muutub keeruliseks. Rakenduse laiendamine muutub raskeks. Sama probleemiga oli silmitsi ka Facebook. Selle probleemi lahendamiseks koostasid nad ühe suuna andmevoo .

Nagu ülaltoodud pildilt näha, on Fluxis kasutatud palju komponente. Vaatame kõik komponendid ükshaaval läbi.

Vaade: see komponent renderdab kasutajaliidese. Alati, kui sellel toimub kasutaja interaktsioon (nagu sündmus), käivitab see toimingu. Ka siis, kui pood teatab View'le, et mõni muudatus on toimunud, renderdab ta ennast uuesti. Näiteks kui kasutaja klõpsab nuppu Lisa .

Toiming: see tegeleb kõigi sündmustega. Need sündmused läbib vaate komponent. Seda kihti kasutatakse tavaliselt API-kõnede tegemiseks. Kui toiming on tehtud, saadetakse see dispetšeri abil. Toiming võib olla midagi sellist nagu postituse lisamine, postituse kustutamine või mis tahes muu kasutaja interaktsioon.

Sündmuse saatmise kasuliku koormuse ühine struktuur on järgmine:

{ actionType: "", data: { title: "Understanding Flux step by step", author: "Sharvin" } }

ActionType võti on kohustuslik ja dispetšer kasutab seda seotud poodi värskenduste edastamiseks. Tuntud on ka konstantide kasutamine võtme actionType väärtuse nime hoidmiseks, et kirjavigu ei tekiks. Andmed sisaldavad sündmuse teavet, mille soovime Actionist Store'isse saata. Selle võtme nimi võib olla mis tahes.

Dispetšer: see on keskne jaotur ja üksikregister. See saadab kasuliku koormuse toimingutest poodi. Samuti hoolitseb selle eest, et toimingu poodi saatmisel ei oleks kaskaadseid efekte. See tagab, et enne andmekihi töötlemise ja salvestamise toimingute lõppu ei toimu muid toiminguid.

Mõelge, et sellel komponendil on süsteemis liikluskontroller. See on tagasihelistamise tsentraliseeritud loend. See kutsub tagasi helistama ja edastab tegevusest saadud kasuliku koormuse.

Selle komponendi tõttu on andmevoog prognoositav. Iga toiming värskendab konkreetset poodi dispetšeris registreeritud tagasihelistamisega.

Store: see hoiab rakenduse olekut ja on selle mustri andmekiht. Ärge pidage seda MVC mudeliks. Rakendusel võib olla üks või mitu rakenduste poodi. Kauplusi värskendatakse, kuna neil on tagasihelistamine, mis on registreeritud dispetšeri abil.

Node'i sündmuste emitterit kasutatakse poe värskendamiseks ja värskenduse edastamiseks vaatamiseks. Vaade ei uuenda rakenduse olekut kunagi otseselt. Seda värskendatakse poe muudatuste tõttu.

See on ainult osa Fluxist, mis saab andmeid värskendada. Poes rakendatavad liidesed on järgmised:

  1. EventEmitter laiendatakse teavitada, et säilitada andmeid on uuendatud.
  2. Kuulajatele meeldivad addChangeListener ja removeChangeListener .
  3. muutuse emiteerimiseks kasutatakse emitChange .

Mõelge ülaltoodud skeemile, kus on rohkem kauplusi ja vaateid. Sellegipoolest on andmete muster ja voog sama. Seda seetõttu, et see on ühesuunaline ja prognoositav andmevoog, erinevalt MVC-st või kahesuunalisest sidumisest. See parandab andmete järjepidevust ja vea leidmine on lihtsam .

Noh, Flux toob ühesuunalise andmevoo abil tabelisse järgmised peamised eelised :

  1. Kood muutub üsna selgeks ja kergesti mõistetavaks.
  2. Ühikutesti abil saab hõlpsasti testida.
  3. Skaleeritavaid rakendusi saab ehitada.
  4. Ennustatav andmevoog.
Märkus: Fluxi ainus puudus on see, et meil on mõni katlaplaat, mille peame kirjutama. Lisaks katlakivile on vähe koodi, mida peame olemasolevale rakendusele komponentide lisamisel kirjutama.

Rakenduse mall

ReaxJS-i voo rakendamise õppimiseks loome Postituste lehe. Siin kuvame kõik postitused. Rakenduse mall on selles pühendumises saadaval. Kasutame seda mallina Fluxi integreerimiseks selle peale.

Selle pühenduse koodi kloonimiseks kasutage järgmist käsku:

git clone //github.com/Sharvin26/DummyBlog.git
git checkout 0d56987b2d461b794e7841302c9337eda1ad0725

Vajame reageerimismarsut ja domineerimismoodulit . Nende pakettide installimiseks kasutage järgmist käsku:

npm install [email protected] [email protected] 

Kui olete valmis, näete järgmist rakendust:

Fluxi üksikasjalikuks mõistmiseks rakendame ainult GET postituste lehte. Kui see on tehtud, saate aru, et POST , EDIT ja DELETE protsess on sama .

Siin näete järgmist kataloogistruktuuri:

+-- README.md +-- package-lock.json +-- package.json +-- node_modules +-- .gitignore +-- public | +-- index.html +-- src | +-- +-- components | +-- +-- +-- common | +-- +-- +-- +-- NavBar.js | +-- +-- +-- PostLists.js | +-- +-- pages | +-- +-- +-- Home.js | +-- +-- +-- NotFound.js | +-- +-- +-- Posts.js | +-- index.js | +-- App.js | +-- db.json
Märkus. Lisasime siia db.json  faili. See on näiv andmefail. Kuna me ei soovi API-sid ehitada ja keskendume selle asemel Fluxile, toome selle faili andmed üles.

Meie rakenduse põhikomponent on index.js. Siin oleme renderdanud avaliku kataloogi App.jssisemuse render- ja getElementById- meetodite abil. Seda kasutatakse marsruutide konfigureerimiseks.index.htmlApp.js

We are also adding NavBar component at the top of the other so it will be available for all the components.

Inside the pages directory we have 3 files =>Home.js, Posts.js, and NotFound.js. Home.js  is simply used to display the Home component. When a user routes to a URL which doesn't exist, then NotFound.js renders.

The Posts.js is the parent component and it is used to get the data from the db.json file. It passes this data to the PostLists.js under the components directory. This component is a dumb component and it only handles the UI. It gets the data as props from its parent component (Posts.js) and displays it in the form of cards.

Now that we are clear about how our blog app is working we will start with integrating Flux on top of it.

Integrating Flux

Install Flux using the following command:

npm install [email protected]

To integrate Flux in our application we will divide this section into 4 subsections:

  1. Dispatcher
  2. Actions
  3. Stores
  4. View

Note: The complete code is available at this repository.

Dispatcher

First, create two new folders named actions and stores under the src directory. After that create a file named appDispatcher.js  under the same src directory.

Note: From now all the files which are related to Flux will have Camel casing as they are not ReactJS components.

Go to the appDispatcher.js and copy-paste the following code:

import { Dispatcher } from "flux"; const dispatcher = new Dispatcher(); export default dispatcher; 

Here we are importing the Dispatcher from the flux library that we installed, creating a new object and exporting it so that our actions module can use it.

Actions

Now go to the actions directory and create two files named actionTypes.js and postActions.js.  In the actionTypes.js we will define the constants that we require in postActions.js and store module.

The reason behind defining constants is that we don't want to make typos. You don't have to define constants but it is generally considered a good practice.

// actionTypes.js export default { GET_POSTS: "GET_POSTS", }; 

Now inside the postActions.js, we will retrieve the data from db.json and use the dispatcher object to dispatch it.

//postActions.js import dispatcher from "../appDispatcher"; import actionTypes from "./actionTypes"; import data from "../db.json"; export function getPosts() { dispatcher.dispatch({ actionTypes: actionTypes.GET_POSTS, posts: data["posts"], }); } 

Here in the above code, we have imported the dispatcher object, actionTypes constant, and data. We are using a dispatcher object's dispatch method to send the data to the store. The data in our case will be sent in the following format:

{ actionTypes: "GET_POSTS", posts: [ { "id": 1, "title": "Hello World", "author": "Sharvin Shah", "body": "Example of blog application" }, { "id": 2, "title": "Hello Again", "author": "John Doe", "body": "Testing another component" } ] }

Stores

Now we need to build the store which will act as a data layer for storing the posts. It will have an event listener to inform the view that something has changed, and will register using dispatcher with the actions to get the data.

Go to the store directory and create a new file called postStore.js.  Now first, we will import EventEmitter from the Events package. It is available in the NodeJS by default. We will also import the dispatcher object and actionTypes constant file here.

import { EventEmitter } from "events"; import dispatcher from "../appDispatcher"; import actionTypes from "../actions/actionTypes"; 

We will declare the constant of the change event and a variable to hold the posts whenever the dispatcher passes it.

const CHANGE_EVENT = "change"; let _posts = [];

Now we will write a class that extends the EventEmitter as its base class. We will declare the following methods in this class:

addChangeListener: It uses the NodeJS EventEmitter.on. It adds a change listener that accepts the callback function.

removeChangeListener: It uses the NodeJS EventEmitter.removeListener. Whenever we don't want to listen for a specific event we use the following method.

emitChange: It uses the NodeJS EventEmitter.emit. Whenever any change occurs, it emits that change.

This class will also have a method called getPosts which returns the variable _posts that we have declared above the class.

Below the variable declaration add the following code:

class PostStore extends EventEmitter { addChangeListener(callback) { this.on(CHANGE_EVENT, callback); } removeChangeListener(callback) { this.removeListener(CHANGE_EVENT, callback); } emitChange() { this.emit(CHANGE_EVENT); } getPosts() { return _posts; } }

Now create the store object of our PostStore class. We will export this object so that we can use it in the view.

const store = new PostStore();

After that, we will use the dispatcher's register method to receive the payload from our Actions component.

To register for the specific event, we need to use the actionTypes value and determine which action has occurred and process the data accordingly. Add the following code below the object declaration:

dispatcher.register((action) => { switch (action.actionTypes) { case actionTypes.GET_POSTS: _posts = action.posts; store.emitChange(); break; default: } });

We will export the object from this module so others can use it.

export default store;

View

Now we will update our view to send the event to postActions  whenever our Posts page is loaded and receive the payload from the postStore. Go to Posts.js under the pages directory. You'll find the following code inside the useEffect method:

useEffect(() => { setposts(data["posts"]); }, []);

We will change how our useEffect reads and updates the data. First, we will use the addChangeListener method from the postStore class and we will pass an onChange callback to it. We will set the postsstate value to have a return value of the getPosts method from the postStore.js file.

At the start, the store will return an empty array as there is no data available. So we will call a getPostsmethod from the postActions.js. This method will read the data and pass it to the store. Then the store will emit the change and addChangeListener will listen to the change and update the value of the posts  in its onChange callback.

If this seems confusing don't worry – check out the flow chart below which makes it easier to understand.

Remove the old code and update the following code inside Posts.js:

import React, { useState, useEffect } from "react"; import PostLists from "../components/PostLists"; import postStore from "../stores/postStore"; import { getPosts } from "../actions/postActions"; function PostPage() { const [posts, setPosts] = useState(postStore.getPosts()); useEffect(() => { postStore.addChangeListener(onChange); if (postStore.getPosts().length === 0) getPosts(); return () => postStore.removeChangeListener(onChange); }, []); function onChange() { setPosts(postStore.getPosts()); } return ( ); } export default PostPage; 

Here you'll find that we have also removed the import and also we are using setPosts inside our callback instead of useEffect method. The return () => postStore.removeChangeListener(onChange); is used to remove the listener once the user leaves that page.

Selle abil minge ajaveebilehele ja leiate, et meie ajaveebi rakendus töötab. Ainus erinevus on see, et nüüd kasutame meetodis useEffect andmete lugemise asemel neid toimingutena, salvestame poodi ja saadame komponentidele, mis seda vajavad.

Tegeliku API kasutamisel leiate, et rakendus laadib API-st andmed korraga ja salvestab need poodi. Sama lehe uuesti külastamisel märkate, et API-kõne pole uuesti vajalik. Saate seda jälgida Chrome'i arendajakonsooli vahekaardil allikas.

Ja oleme valmis !! Loodan, et see õpetus on muutnud Fluxi idee selgemaks ja saate seda oma projektides kasutada.

Võtke julgelt ühendust Twitteris ja Githubis.