Sådan bruges Flux til at styre tilstand i ReactJS - forklaret med et eksempel

Hvis du er begyndt at arbejde på ReactJS for nylig, undrer du dig måske over, hvordan du administrerer tilstanden i React, så din applikation kan skaleres.

For at løse dette statsadministrationsproblem har mange virksomheder og mennesker udviklet forskellige løsninger. Facebook, der udviklede ReactJS, kom med en løsning kaldet Flux .

Du har muligvis hørt om Redux, hvis du har arbejdet med frontend-teknologi som AngularJS eller EmberJS . ReactJS har også et bibliotek til implementering af Redux.

Men før jeg lærte Redux, vil jeg råde dig til at gennemgå Flux og forstå det. Derefter skal du prøve Redux. Jeg siger dette, fordi Redux er en mere avanceret version af Flux. Hvis begreberne Flux er klare, kan du lære redux og integrere det i din applikation.

Hvad er flux?

Flux bruger et ensrettet datastrømningsmønster for at løse tilstandsstyringskompleksitet. Husk, at det ikke er en ramme - snarere er det mere et mønster, der er målrettet mod at løse statsadministrationsproblemet.

Spekulerer du på, hvad der er galt med den eksisterende MVC-ramme? Forestil dig, at din klients applikation skaleres op. Du har interaktion mellem mange modeller og synspunkter. Hvordan ville det se ud?

Forholdet mellem komponenter bliver kompliceret. Det bliver svært at skalere applikationen. Facebook stod over for det samme problem. For at løse dette problem arkitekterede de en enkelt retningsbestemt datastrøm .

Som du kan se på billedet ovenfor, er der mange komponenter, der bruges i Flux. Lad os gennemgå alle komponenterne en efter en.

Visning: denne komponent gengiver brugergrænsefladen. Hver gang der forekommer brugerinteraktion på det (som en begivenhed), affyrer det handlingen. Også når butikken informerer visningen om, at der er sket en ændring, gengiver den sig selv igen. For eksempel, hvis en bruger klikker på knappen Tilføj .

Handling: dette håndterer alle begivenheder. Disse begivenheder overføres af visningskomponenten. Dette lag bruges generelt til at foretage API-opkald. Når handlingen er udført, sendes den ved hjælp af Dispatcher. Handlingen kan være noget som at tilføje et indlæg, slette et indlæg eller enhver anden brugerinteraktion.

Den fælles struktur for nyttelasten til afsendelse af en begivenhed er som følger:

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

ActionType-nøglen er obligatorisk, og den bruges af afsenderen til at videregive opdateringer til den relaterede butik. Det er også en kendt praksis at bruge konstanter til at holde værdienavnet for actionType-nøglen, så der ikke forekommer skrivefejl. Data indeholder de hændelsesoplysninger, som vi vil sende fra handling til butik. Navnet på denne nøgle kan være hvad som helst.

Dispatcher: dette er det centrale hub og singleton-registreringsdatabasen. Den sender nyttelasten fra Handlinger til Butik. Sørg også for, at der ikke er nogen kaskadevirkninger, når en handling sendes til butikken. Det sikrer, at der ikke sker nogen anden handling, før datalaget har afsluttet behandling og lagring.

Overvej, at denne komponent har en trafikcontroller i systemet. Det er en central liste over tilbagekald. Det påkalder tilbagekaldelsen og sender den nyttelast, den har modtaget fra handlingen.

På grund af denne komponent er datastrømmen forudsigelig. Hver handling opdaterer den specifikke butik med det tilbagekald, der er registreret hos afsenderen.

Store: dette holder apptilstanden og er et datalag af dette mønster. Overvej det ikke som en model fra MVC. En applikation kan have en eller flere appbutikker. Butikker bliver opdateret, fordi de har et tilbagekald, der registreres ved hjælp af afsenderen.

Node's hændelsesemitter bruges til at opdatere butikken og udsende opdateringen til visning. Visningen opdaterer aldrig applikationens tilstand direkte. Det opdateres på grund af ændringer i butikken.

Dette er kun en del af Flux, der kan opdatere dataene. Grænseflader implementeret i butikken er som følger:

  1. Den EventEmitter udvides til at informere den opfattelse, at lagre data er blevet opdateret.
  2. Lyttere som addChangeListener og removeChangeListener tilføjes.
  3. emitChange bruges til at udsende ændringen.

Overvej ovenstående diagram med flere butikker og visninger. Stadig vil mønsteret og datastrømmen være det samme. Dette skyldes, at dette er en enkelt retning og forudsigelig datastrøm i modsætning til MVC eller tovejsbinding. Dette forbedrer datakonsistensen, og det er lettere at finde fejlen .

Flux bringer følgende vigtige fordele til tabellen ved hjælp af ensrettet dataflyt:

  1. Koden bliver ret klar og let at forstå.
  2. Let testbar ved hjælp af enhedstest.
  3. Skalerbare apps kan bygges.
  4. Forudsigelig datastrøm.
Bemærk: Den eneste ulempe ved Flux er, at der er en kedelplade, som vi skal skrive. Udover kogepladen er der kun lidt kode, vi skal skrive, når vi tilføjer komponenter til den eksisterende applikation.

Ansøgningsskabelon

For at lære at implementere flux i ReactJS vil vi oprette en indlægsside. Her viser vi alle indlæg. Ansøgningsskabelonen er tilgængelig ved denne forpligtelse. Vi bruger dette som skabelon til at integrere Flux oven på det.

For at klone koden fra denne forpligtelse skal du bruge følgende kommando:

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

Vi kræver et reaktor-router-dom og bootstrap- modul. For at installere disse pakker skal du bruge følgende kommando:

npm install [email protected] [email protected] 

Når du er færdig, ser du følgende applikation:

For at forstå Flux i detaljer implementerer vi kun siden GET- indlæg. Når det er gjort, vil du indse, at processen er den samme for POST , EDIT og SLET .

Her ser du følgende katalogstruktur:

+-- 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
Bemærk: Vi har tilføjet en db.json  fil her. Dette er en dummy datafil. Da vi ikke ønsker at opbygge API'er og i stedet fokusere på Flux, henter vi dataene fra denne fil.

Vores applikations basiskomponent er index.js. Her har vi gengivet App.jsindersiden af ​​den index.htmlunder offentlige bibliotek ved hjælp af render- og getElementById- metoderne. Den App.jsbruges til at konfigurere ruterne.

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.

Gå til blogsiden, så finder du ud af, at vores blog-app fungerer. Den eneste forskel er, at vi i stedet for at læse dataene i useEffect- metoden nu læser dem i handlinger, gemmer dem i butikken og sender dem til de komponenter, der kræver det.

Når du bruger den aktuelle API, finder du ud af, at applikationen indlæser dataene fra API'en en gang og gemmer dem i butikken. Når vi besøger den samme side igen, bemærker du, at der ikke kræves noget API-opkald igen. Du kan overvåge det under kilde-fanen i Chrome Developer-konsollen.

Og vi er færdige !! Jeg håber, at denne tutorial har gjort idéen om Flux klarere, og du vil være i stand til at bruge den i dine projekter.

Du er velkommen til at oprette forbindelse til mig på Twitter og Github.