flux 状态_如何在ReactJS中使用Flux来管理状态-用示例解​​释

flux 状态

If you have started working on ReactJS recently then you might be wondering how to manage state in React so that your application can scale.

如果您最近开始使用ReactJS,那么您可能想知道如何在React中管理状态,以便您的应用程序可以扩展。

To solve this state management issue, many companies and people have developed various solutions. Facebook, who developed ReactJS, came up with a solution called Flux.

为了解决这个状态管理问题,许多公司和人们已经开发出各种解决方案。 开发ReactJS的Facebook提出了一个名为Flux的解决方案。

You may have heard about Redux if you have worked on front end technology such as AngularJS or EmberJS. ReactJS also has a library for implementing Redux.

如果您从事过诸如AngularJSEmberJS之类的前端技术,那么您可能已经听说过Redux 。 ReactJS还有一个用于实现Redux的库。

But before learning Redux I would advise you to go through Flux and understand it. After that give Redux a try. I say this because Redux is a more advanced version of Flux. If the concepts of Flux are clear then you can learn redux and integrate it into your application.

但是在学习Redux之前,我建议您先阅读Flux并了解它。 之后,尝试Redux。 我之所以这样说是因为Redux是Flux的更高级版本。 如果通量的概念很明确,那么您可以学习redux并将其集成到您的应用程序中。

什么是助焊剂? (What is flux?)

Flux uses a unidirectional data flow pattern to solve state management complexity. Remember it is not a framework – rather it's more of a pattern that targets to solve the state management issue.

Flux使用单向数据流模式来解决状态管理的复杂性。 请记住,这不是一个框架,而是一种旨在解决状态管理问题的模式。

Are you wondering what's wrong with the existing MVC framework? Imagine your client's application scales up. You have interaction between many models and views. How would it look?

您是否想知道现有的MVC框架有什么问题? 想象一下您的客户的应用程序可以扩展。 您需要在许多模型和视图之间进行交互。 看起来如何?

The relationship between components gets complicated. It becomes hard to scale the application. Facebook faced the same issue. To solve this issue they architected a Single directional data flow.

组件之间的关系变得复杂。 扩展应用程序变得困难。 Facebook也面临同样的问题。 为了解决这个问题,他们设计了单向数据流

As you can see from the image above, there are a lot of components used in Flux. Let's go through all the components one by one.

从上图可以看到,Flux中使用了许多组件。 让我们一一介绍所有组件。

View: this component renders the UI. Whenever any user interaction occurs on it (like an event) then it fires off the action. Also when the Store informs the View that some change has occurred, it re-renders itself. For example, if a user clicks the Add button.

视图:此组件呈现UI。 每当发生任何用户交互(例如事件)时,它都会触发该操作。 同样,当商店通知视图发生了某些更改时,它也会重新呈现自己。 例如,如果用户单击添加按钮。

Action: this handles all the events. These events are passed by the view component. This layer is generally used to make API calls. Once the action is done it is dispatched using the Dispatcher. The action can be something like add a post, delete a post, or any other user interaction.

行动:这将处理所有事件。 这些事件由视图组件传递。 该层通常用于进行API调用。 操作完成后,将使用分派器将其分派。 该操作可以是添加帖子,删除帖子或任何其他用户交互之类的操作。

The common structure of the payload for dispatching an event is as follows:

用于调度事件的有效负载的通用结构如下:

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

The actionType key is compulsory and it is used by the dispatcher to pass updates to the related store. It is also a known practice to use constants to hold the value name for actionType key so no typos occur. Data holds the event information that we want to dispatch from Action to Store. The name for this key can be anything.

actionType键是强制性的,调度程序使用该键将更新传递给相关存储。 使用常量来保存actionType键的值名称也是众所周知的做法,这样就不会出现拼写错误。 数据包含我们要从操作分发到商店的事件信息。 该密钥的名称可以是任何名称。

Dispatcher: this is the central hub and singleton registry. It dispatches the payload from Actions to Store. Also makes sure that there are no cascading effects when an action is dispatched to the store. It ensures that no other action happens before the data layer has completed processing and storing operations.

调度程序:这是中央中心和单例注册表。 它将有效负载从操作分派到存储。 还应确保在将操作发送到商店时没有级联效果。 它确保在数据层完成处理和存储操作之前不会发生其他任何动作。

Consider this component has a traffic controller in the system. It is a centralized list of callbacks. It invokes the callback and broadcasts the payload it received from the action.

考虑该组件在系统中具有流量控制器。 它是回调的集中列表。 它调用回调并广播从操作接收到的有效负载。

Due to this component, the data flow is predictable. Every action updates the specific store with the callback that is registered with the dispatcher.

由于此组件,数据流是可预测的。 每个操作都使用在调度程序中注册的回调更新特定的存储。

Store: this holds the app state and is a data layer of this pattern. Do not consider it as a model from MVC. An application can have one or many app stores. Stores get updated because they have a callback that is registered using the dispatcher.

Store:保存应用程序状态,是此模式的数据层。 不要将其视为来自MVC的模型。 一个应用程序可以有一个或多个应用程序商店。 由于商店具有使用分派器注册的回调,因此商店得到更新。

Node's event emitter is used to update the store and broadcast the update to view. The view never directly updates the application state. It is updated because of the changes to the store.

Node的事件发射器用于更新商店并广播更新以供查看。 视图从不直接更新应用程序状态。 由于存储的更改,它已更新。

This is only part of Flux that can update the data. Interfaces implemented in the store are as follows:

这只是Flux可以更新数据的一部分。 商店中实现的接口如下:

  1. The EventEmitter is extended to inform the view that store data has been updated.

    扩展了EventEmitter ,以通知视图商店数据已更新。

  2. Listeners like addChangeListener and removeChangeListener are added.

    添加了诸如addChangeListenerremoveChangeListener之类的侦听器。

  3. emitChange is used to emit the change.

    glowChange用于发出更改。

Consider the above diagram with more stores and views. Still, the pattern and the flow of data will be the same. This is because this is a single direction and predictable data flow, unlike MVC or Two-way binding. This improves the data consistency and it's easier to find the bug.

考虑以上带有更多存储和视图的图。 尽管如此,模式和数据流将是相同的。 这是因为与MVC或双向绑定不同,这是一个单一方向且可预测的数据流。 这样可以提高数据一致性 ,并且更容易发现错误

Well, Flux brings the following key benefits to the table with the help of unidirectional data flow:

好吧,Flux在单向数据流的帮助下为表带来了以下主要好处

  1. The code becomes quite clear and easy to understand.

    代码变得非常清晰和易于理解。
  2. Easily testable using Unit Test.

    使用单元测试可以轻松测试。
  3. Scalable apps can be built.

    可以构建可扩展的应用程序。
  4. Predictable data flow.

    可预测的数据流。

Note: The only drawback with the Flux is that there is some boilerplate that we need to write. Besides the boilerplate, there is little code we need to write when adding components to the existing application.

注意: Flux的唯一缺点是我们需要编写一些样板文件。 除了样板之外,在向现有应用程序中添加组件时,我们几乎不需要编写任何代码。

应用模板 (Application Template)

To learn how to implement flux in ReactJS, we will build a Posts page. Here we will display all the posts. The application template is available at this commit. We will use this as the template for integrating Flux on top of it.

要学习如何在ReactJS中实现流量,我们将构建一个Posts页面。 在这里,我们将显示所有帖子。 应用程序模板在此提交时可用。 我们将以此为模板在其上集成Flux。

To clone the code from this commit, use the following command:

要从此提交克隆代码,请使用以下命令:

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

We will require a react-router-dom and bootstrap module. To install these packages, use the following command:

我们将需要一个react-router-dombootstrap模块。 要安装这些软件包,请使用以下命令:

npm install react-router-dom@5.0.0 bootstrap@4.3.1

Once done you'll see the following application:

完成后,您将看到以下应用程序:

To understand Flux in detail we will only implement the GET posts page. Once that is done you'll realize the process is the same for POST, EDIT and DELETE.

要详细了解Flux,我们将仅实现GET posts页面。 完成后,您将意识到POSTEDITDELETE的过程是相同的。

Here you'll see the following directory structure:

在这里,您将看到以下目录结构:

+-- 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

Note: We have added here a db.json  file. This is a dummy data file. Since we don't want to build APIs and instead focus on Flux, we will retrieve the data from this file.

注意:我们在此处添加了db.json文件。 这是一个虚拟数据文件。 由于我们不想构建API,而是专注于Flux,因此我们将从该文件中检索数据。

Our Application's base component is index.js. Here we have rendered the App.js inside the index.html under public directory using the render and getElementById methods. The App.js is used for configuring the routes.

我们应用程序的基本组件是index.js 。 在这里,我们已使用rendergetElementById方法在公共目录下的index.html呈现了App.js App.js用于配置路由。

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

我们还将NavBar组件添加到另一个组件的顶部,以便所有组件均可使用。

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.

pages目录中,我们有3个文件=> Home.jsPosts.jsNotFound.jsHome.js仅用于显示Home组件。 当用户路由到不存在的URL时,将呈现NotFound.js

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.

Posts.js是父组件,它用于从db.json文件获取数据。 它将这些数据传递到components目录下的PostLists.js 。 该组件是一个哑组件,仅处理UI。 它从其父组件( Posts.js )获取数据作为道具,并以卡片形式显示。

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

现在我们已经清楚了博客应用程序的工作方式,我们将从在其之上集成Flux开始。

积分通量 (Integrating Flux)

Install Flux using the following command:

使用以下命令安装Flux:

npm install flux@3.1.3

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

为了将Flux集成到我们的应用程序中,我们将本节分为4个小节:

  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.

首先,创建两个名为action的新文件夹,并将其存储src目录下。 之后,在同一src目录下创建一个名为appDispatcher.js的文件。

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

注意:从现在开始,所有与Flux相关的文件都将使用Camel大小写,因为它们不是ReactJS组件。

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

转到appDispatcher.js并复制粘贴以下代码:

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.

在这里,我们从安装的流量库中导入Dispatcher,创建一个新对象并将其导出,以便我们的action模块可以使用它。

动作 (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.

现在转到actions目录,并创建两个名为actionTypes.jspostActions.js文件。 在actionTypes.js我们将在postActions.js和store模块中定义所需的常量。

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内部,我们将从db.json检索数据,并使用调度程序对象进行调度。

//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常量和数据。 我们正在使用调度程序对象的调度方法将数据发送到商店。 本例中的数据将以以下格式发送:

{
	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.

转到商店目录并创建一个名为postStore.js的新文件。 现在,首先,我们将从事件包中导入EventEmitter 。 默认情况下,它在NodeJS中可用。 我们还将在此处导入调度程序对象和actionTypes常量文件。

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.

每当调度程序通过它时,我们将声明change事件的常量和一个变量以保存帖子。

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:

现在,我们将编写一个将EventEmitter扩展为其基类的类。 我们将在此类中声明以下方法:

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

addChangeListener :它使用NodeJS EventEmitter.on 。 它添加了一个接受回调函数的更改侦听器。

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

removeChangeListener :它使用NodeJS EventEmitter.removeListener 每当我们不想听特定事件时,我们都使用以下方法。

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

emitChange :它使用NodeJS EventEmitter.emit 每当发生任何更改时,它都会发出更改。

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

此类还将具有一个名为getPosts的方法,该方法返回我们在该类之上声明的变量_posts

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.

现在,创建我们的PostStore类的store对象。 我们将导出该对象,以便可以在视图中使用它。

const store = new PostStore();

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

之后,我们将使用调度程序的register方法从Actions组件接收有效负载。

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:

要注册特定事件,我们需要使用actionTypes值并确定发生了哪个操作并相应地处理数据。 在对象声明下面添加以下代码:

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:

现在,我们将更新视图,以在加载“ Posts”页面时将事件发送到postActions并从postStore接收有效负载。 转到页面目录下的Posts.js 。 您可以在useEffect方法中找到以下代码:

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 posts state value to have a return value of the getPosts method from the postStore.js file.

我们将更改useEffect读取和更新数据的方式。 首先,我们将使用postStore类中的addChangeListener方法,并将一个onChange回调传递给它。 我们将设置posts 状态值,以获取postStore.js文件中getPosts方法的返回值。

At the start, the store will return an empty array as there is no data available. So we will call a getPosts method 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.

首先,由于没有可用数据,存储将返回一个空数组。 所以我们将调用getPosts postActions.js方法。 此方法将读取数据并将其传递到存储。 然后,商店将发出更改,而addChangeListener将侦听更改并更新其onChange回调中的posts值。

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:

删除旧代码并更新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 (
        <div>
            <PostLists posts={posts} />
        </div>
    );
}

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.

在这里,您会发现我们也删除了导入,并且在回调中使用setPosts而不是useEffect方法。 return () => postStore.removeChangeListener(onChange); 用于在用户离开该页面后删除该侦听器。

With this go to the Blog Page and you'll find that our blog app is working. The only difference is that now instead of reading the data in the useEffect method we are reading it in actions, storing it in the store, and sending it to the components that require it.

转到博客页面,您会发现我们的博客应用正在运行。 唯一的区别是,现在,我们不是在useEffect方法中读取数据,而是在操作中读取数据,将其存储在存储中,并将其发送到需要它的组件。

When using the actual API you'll find that the application loads the data from the API one time and stores it in the store. When we revisit the same page you'll observe that no API call is required again. You can monitor it under the source tab in Chrome Developer console.

使用实际的API时,您会发现该应用程序一次从API加载了数据并将其存储在商店中。 当我们再次访问同一页面时,您会发现不再需要API调用。 您可以在Chrome开发者控制台的“来源”标签下对其进行监控。

And we're done!! I hope this tutorial has made the idea of Flux clearer and you'll be able to use it in your projects.

我们完成了! 我希望本教程使Flux的概念更清晰,并且您可以在项目中使用它。

Feel free to connect with me on Twitter and Github.

随时在TwitterGithub上与我联系。

翻译自: https://www.freecodecamp.org/news/how-to-use-flux-in-react-example/

flux 状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值