A Flux használata az állapot kezeléséhez a ReactJS-ben - egy példával elmagyarázva

Ha a közelmúltban kezdett el dolgozni a ReactJS-en, akkor elgondolkodhat azon, hogyan lehet a React állapotát kezelni, hogy az alkalmazás méretezhető legyen.

Ennek az államvezetési kérdésnek a megoldására sok vállalat és ember dolgozott ki különféle megoldásokat. A Facebook, aki kifejlesztette a ReactJS-t, a Flux nevű megoldással állt elő .

Lehet, hogy hallott Redux , ha már dolgozott front end technológiák, mint a AngularJS vagy EmberJS . A ReactJS rendelkezik egy könyvtárral is a Redux megvalósításához.

De a Redux elsajátítása előtt azt tanácsolom, hogy menjen át a Fluxon és értse meg. Ezután próbáljon Reduxot. Ezt azért mondom, mert a Redux a Flux fejlettebb verziója. Ha a Flux fogalmai egyértelműek, akkor megtanulhatja a reduxot és integrálhatja azt az alkalmazásába.

Mi a fluxus?

A Flux egyirányú adatfolyam-mintát használ az állapotkezelés bonyolultságának megoldására. Ne feledje, hogy ez nem keretrendszer - inkább mintázat, amely az államigazgatás kérdésének megoldására irányul.

Kíváncsi arra, hogy mi a baj a meglévő MVC keretrendszerrel? Képzelje el, hogy az ügyfelek alkalmazásának méretaránya felnő. Sok modell és nézet között van interakció. Hogy nézne ki?

Az összetevők közötti kapcsolat bonyolulttá válik. Nehéz lesz méretezni az alkalmazást. A Facebook is ugyanazzal a problémával szembesült. Ennek a kérdésnek a megoldására egyirányú adatáramlást terveztek .

Amint a fenti képen is látható, nagyon sok komponenst használnak a Fluxban. Nézzük végig az összes alkatrészt egyenként.

Nézet: ez az összetevő megjeleníti a felhasználói felületet. Valahányszor bármilyen felhasználói interakció történik rajta (például egy eseményen), ez elindítja a műveletet. Akkor is, amikor az áruház tájékoztatja a Nézetet arról, hogy valamilyen változás történt, akkor újra megjeleníti önmagát. Például, ha egy felhasználó a Hozzáadás gombra kattint.

Művelet: ez kezeli az összes eseményt. Ezeket az eseményeket a nézet komponens adja át. Ezt a réteget általában API hívások kezdeményezésére használják. Miután a művelet megtörtént, a Dispatcher segítségével küldi el. A művelet lehet például egy bejegyzés hozzáadása, egy bejegyzés törlése vagy bármilyen más felhasználói interakció.

Az esemény feladásához a hasznos teher közös szerkezete a következő:

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

Az actionType kulcs kötelező, és a diszpécser arra használja, hogy továbbítsa a frissítéseket a kapcsolódó tárolóhoz. Ismert gyakorlat az is, hogy konstansokat használunk az actionType kulcs értéknevének megtartására, így elírások nem történnek. Az adatok tartalmazzák azokat az eseményinformációkat, amelyeket az Action to Store-ba akarunk küldeni. A kulcs neve bármi lehet.

Diszpécser: ez a központi hub és az egyedi regisztráció. Elküldi a hasznos terhet az Actions-ból Store-ba. Arról is gondoskodik, hogy ne legyenek lépcsőzetes hatások, amikor egy műveletet a boltba küldünk. Biztosítja, hogy más művelet ne történjen, mielőtt az adatréteg befejezte a feldolgozási és tárolási műveleteket.

Vegye figyelembe, hogy ennek az összetevőnek van forgalomszabályozója a rendszerben. Ez a visszahívások központosított listája. Meghívja a visszahívást és sugározza a műveletből kapott hasznos terhet.

Ennek a komponensnek köszönhetően az adatáramlás kiszámítható. Minden művelet frissíti az adott üzletet a diszpécsernél regisztrált visszahívással.

Store: ez tartja az alkalmazás állapotát, és ennek a mintának az adatrétege. Ne tekintsük az MVC modelljének. Egy alkalmazásnak lehet egy vagy több alkalmazásboltja. Az üzletek frissülnek, mert visszahívásuk van, amelyet a diszpécser regisztrál.

A Node eseménykibocsátója az áruház frissítésére és a frissítés közzétételére szolgál. A nézet soha nem frissíti közvetlenül az alkalmazás állapotát. Az áruház változásai miatt frissül.

Ez csak a Flux része, amely képes frissíteni az adatokat. Az üzletben megvalósított interfészek a következők:

  1. Az EventEmitter kibővítve tájékoztatja a nézetet arról, hogy az áruház adatai frissültek.
  2. Az olyan hallgatók, mint az addChangeListener és az removeChangeListener , hozzáadódnak.
  3. Az emitChange a változás kibocsátására szolgál.

Vegye figyelembe a fenti diagramot, ahol több üzlet és nézet található. Ennek ellenére az adatminta és az adatfolyam ugyanaz lesz. Ez azért van, mert ez egyirányú és kiszámítható adatáramlás, ellentétben az MVC-vel vagy a kétirányú kötéssel. Ez javítja az adatok konzisztenciáját és könnyebben megtalálja a hibát .

Nos, a Flux a következő fő előnyöket hozza a táblázatba egyirányú adatáramlás segítségével :

  1. A kód meglehetősen egyértelművé és könnyen érthetővé válik.
  2. Könnyen tesztelhető az Unit Test segítségével.
  3. Skálázható alkalmazások építhetők.
  4. Kiszámítható adatáramlás.
Megjegyzés: A Flux egyetlen hátránya, hogy van néhány kazán, amelyet meg kell írnunk. A kazántáblán kívül kevés kódot kell írnunk, amikor összetevőket adunk a meglévő alkalmazáshoz.

Alkalmazássablon

Hogy megtudjuk, hogyan kell megvalósítani a fluxust a ReactJS-ben, létrehozunk egy Bejegyzések oldalt. Itt az összes bejegyzést megjelenítjük. Az alkalmazássablon ezen a vállalaton érhető el. Ezt fogjuk használni a sablonként a Flux integrálásához a tetején.

A kód klónozásához erről az elkötelezettségről használja a következő parancsot:

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

Szükségünk lesz egy reakció-router-dom és bootstrap modulra. A csomagok telepítéséhez használja a következő parancsot:

npm install [email protected] [email protected] 

Miután elkészült, a következő alkalmazás jelenik meg:

A Flux részletes megértése érdekében csak a GET posts oldalt valósítjuk meg . Miután ez megtörtént, rájössz, hogy a POST , EDIT és DELETE folyamatok megegyeznek .

Itt láthatja a következő könyvtárstruktúrát:

+-- 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
Megjegyzés: Hozzáadtunk egy db.json  fájlt. Ez egy dummy adatfájl. Mivel nem API-kat akarunk létrehozni, hanem a Fluxra kell összpontosítanunk, az adatokat lekérjük erről a fájlról.

Alkalmazásunk alapkomponense a index.js. Itt tették a App.jsbelsejében a index.htmlközjogi könyvtárat a render és getElementById módszerekkel. Az App.jsaz útvonalak konfigurálására szolgál.

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.

Ezzel lépjen a Blog oldalra, és rájön, hogy blogalkalmazásunk működik. Az egyetlen különbség az, hogy most ahelyett, hogy olvassuk az adatokat a useEffect metódusban, műveletekként olvassuk, tároljuk a boltban és elküldjük azokat az összetevőket, amelyekhez szükség van.

A tényleges API használatakor rájön, hogy az alkalmazás egyszerre tölti be az API-ból származó adatokat, és tárolja azokat az üzletben. Amikor újra felkeressük ugyanazt az oldalt, akkor észreveheti, hogy újra nincs szükség API-hívásra. Megfigyelheti a Chrome fejlesztői konzol forrás fülén.

És készen vagyunk !! Remélem, hogy ez a bemutató világosabbá tette a Flux ötletét, és képes lesz használni a projektjeiben.

Lépjen kapcsolatba velem a Twitteren és a Github-on.