Kaip naudoti srautą valdant būseną „ReactJS“ - paaiškinta pavyzdžiu

Jei neseniai pradėjote dirbti su „ReactJS“, jums gali būti įdomu, kaip valdyti „React“ būseną, kad jūsų programa galėtų išplėsti.

Norėdami išspręsti šį valstybės valdymo klausimą, daugelis įmonių ir žmonių sukūrė įvairius sprendimus. „ReactJS“ sukūręs „Facebook“ sugalvojo „ Flux“ pavadinimą .

Galbūt girdėjote apie „ Redux“, jei dirbote su priekinės technologijos, tokios kaip „ AngularJS“ ar „ EmberJS“, technologijomis . „ReactJS“ taip pat turi „Redux“ diegimo biblioteką.

Bet prieš išmokdamas „Redux“, patarčiau pereiti per „Flux“ ir tai suprasti. Po to pabandykite Redux. Aš tai sakau, nes „Redux“ yra tobulesnė „Flux“ versija. Jei „Flux“ sąvokos yra aiškios, galite išmokti „redux“ ir integruoti ją į savo programą.

Kas yra srautas?

„Flux“ naudoja vienkryptį duomenų srauto modelį, kad išspręstų valstybės valdymo sudėtingumą. Atminkite, kad tai nėra pagrindas - greičiau tai yra modelis, kuriuo siekiama išspręsti valstybės valdymo problemą.

Ar įdomu, kas yra blogai esamoje MVC sistemoje? Įsivaizduokite, kad jūsų kliento programa didėja. Jūs sąveikaujate su daugeliu modelių ir rodinių. Kaip tai atrodytų?

Santykis tarp komponentų komplikuojasi. Tampa sunku išplėsti taikymą. „Facebook“ susidūrė su ta pačia problema. Norėdami išspręsti šią problemą, jie sukūrė vieną krypties duomenų srautą .

Kaip matote iš aukščiau esančio paveikslėlio, „Flux“ naudojama daugybė komponentų. Peržiūrėkime visus komponentus po vieną.

Rodinys: šis komponentas pateikia vartotojo sąsają. Kai tik įvyksta bet kokia vartotojo sąveika (pvz., Įvykis), tai iššaukia veiksmą. Be to, kai Parduotuvė praneša „View“, kad įvyko tam tikrų pakeitimų, ji vėl pateikia save. Pavyzdžiui, jei vartotojas spusteli mygtuką Pridėti .

Veiksmas: tai tvarko visus įvykius. Šiuos įvykius perduoda rodinio komponentas. Šis sluoksnis paprastai naudojamas skambinant API. Kai veiksmas bus atliktas, jis bus išsiųstas naudojant dispečerą. Veiksmas gali būti panašus į įrašo pridėjimą, įrašo ištrynimą ar bet kurią kitą vartotojo sąveiką.

Įvykio išsiuntimo naudingosios apkrovos bendra struktūra yra tokia:

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

„ActionType“ raktas yra privalomas, o dispečeris jį naudoja perduoti atnaujinimus susijusiai parduotuvei. Taip pat žinoma, kad naudojant konstantas laikomas „actionType“ rakto vertės pavadinimas, kad nebūtų klaidų. Duomenyse yra įvykio informacija, kurią norime siųsti iš „Action to Store“. Šio rakto pavadinimas gali būti bet koks.

Dispečeris: tai yra centrinis mazgas ir pavienis registras. Tai siunčia naudingąją apkrovą iš „Actions to Store“. Taip pat įsitikina, kad išsiunčiant veiksmą į parduotuvę nėra kaskadinių efektų. Tai užtikrina, kad jokie kiti veiksmai neįvyktų, kol duomenų sluoksnis neužbaigs apdorojimo ir saugojimo operacijų.

Apsvarstykite, ar šis komponentas sistemoje turi eismo valdiklį. Tai centralizuotas atgalinių skambučių sąrašas. Jis iškviečia atgalinį skambutį ir transliuoja iš veiksmo gautą naudingąją apkrovą.

Dėl šio komponento duomenų srautas yra nuspėjamas. Kiekvienas veiksmas atnaujina konkrečią parduotuvę su atgaliniu skambučiu, kuris yra užregistruotas dispečeryje.

Parduotuvė: tai palaiko programos būseną ir yra šio modelio duomenų sluoksnis. Nelaikykite to MVC modeliu. Programa gali turėti vieną ar kelias programų parduotuves. Parduotuvės atnaujinamos, nes jos turi atgalinį skambutį, kuris yra užregistruotas naudojant dispečerį.

„Node“ įvykių skleidėjas naudojamas parduotuvei atnaujinti ir atnaujinimui transliuoti, kad būtų galima peržiūrėti. Rodinys niekada tiesiogiai neatnaujina programos būsenos. Jis atnaujinamas dėl parduotuvės pakeitimų.

Tai tik „Flux“ dalis, galinti atnaujinti duomenis. Parduotuvėje įdiegtos tokios sąsajos:

  1. EventEmitter“ yra išplėstas informuoti rodinį, kad parduotuvės duomenys atnaujinti.
  2. Klausytojai, kaip „ addChangeListener“ ir „ removeChangeListener“ , pridedami.
  3. „emitChange “ naudojamas norint išleisti pakeitimą.

Apsvarstykite aukščiau pateiktą diagramą su daugiau parduotuvių ir rodinių. Vis dėlto duomenų modelis ir srautas bus vienodi. Taip yra todėl, kad tai yra viena kryptis ir nuspėjamas duomenų srautas, skirtingai nei MVC ar dvipusis susiejimas. Tai pagerina duomenų nuoseklumą ir lengviau rasti klaidą .

Na, „Flux“ suteikia vienkartinio duomenų srauto lentelėje šiuos pagrindinius privalumus :

  1. Kodas tampa gana aiškus ir lengvai suprantamas.
  2. Lengvai patikrinama naudojant „Unit Test“.
  3. Galima sukurti keičiamo dydžio programas.
  4. Nuspėjamas duomenų srautas.
Pastaba: Vienintelis „Flux“ trūkumas yra tas, kad turime tam tikrą katilinę, kurią turime parašyti. Be katilinės, yra nedaug kodo, kurį turime parašyti pridedant komponentus prie esamos programos.

Programos šablonas

Norėdami sužinoti, kaip įgyvendinti srautą „ReactJS“, sukursime „Pranešimų“ puslapį. Čia mes parodysime visus įrašus. Programos šabloną galite rasti šiame įsipareigojime. Mes tai naudosime kaip „Flux“ integravimo ant jo šabloną.

Norėdami klonuoti kodą iš šio įsipareigojimo, naudokite šią komandą:

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

Reikės „ rout-router-dom“ ir „ bootstrap“ modulio. Norėdami įdiegti šiuos paketus, naudokite šią komandą:

npm install [email protected] [email protected] 

Kai tai padarysite, pamatysite šią programą:

Norėdami išsamiai suprasti „Flux“, mes įdiegsime tik GET žinučių puslapį. Tai padarę suprasite, kad POST , EDIT ir DELETE procesas yra tas pats .

Čia pamatysite šią katalogo struktūrą:

+-- 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
Pastaba: mes čia pridėjome db.json  failą. Tai yra fiktyvus duomenų failas. Kadangi mes nenorime kurti API, o daugiau dėmesio skirti „Flux“, duomenis gausime iš šio failo.

Pagrindinis mūsų programos komponentas yra index.js. Čia mes atvaizdavome viešojo katalogo App.jsvidų index.htmlnaudodami render ir getElementById metodus. Naudojamas App.jsmaršrutams konfigūruoti.

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.

Tai eikite į tinklaraščio puslapį ir pamatysite, kad mūsų tinklaraščio programa veikia. Vienintelis skirtumas yra tas, kad dabar, užuot skaitę „useEffect“ metodo duomenis, mes juos skaitome veiksmais, saugome parduotuvėje ir siunčiame komponentams, kuriems to reikia.

Naudodami tikrąją API pastebėsite, kad programa vieną kartą įkelia duomenis iš API ir saugo juos parduotuvėje. Kai dar kartą apsilankysime tame pačiame puslapyje, pastebėsite, kad API skambutis vėl nereikalingas. Ją galite stebėti „Chrome Developer Console“ skirtuke šaltinis.

Ir mes baigėme !! Tikiuosi, kad ši pamoka išaiškino „Flux“ idėją ir galėsite ją naudoti savo projektuose.

Nedvejodami susisiekite su manimi „Twitter“ ir „Github“.