Redux中的排队操作 [英] Queuing Actions in Redux

查看:87
本文介绍了Redux中的排队操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前遇到的情况是我需要连续运行Redux Actions。我已经看过各种各样的中间件,比如redux-promise,如果你知道连续动作在根点(缺少一个更好的术语)被触发的行为,这似乎很好 / em>。

I've currently got a situation whereby I need Redux Actions to be run consecutively. I've taken a look at various middlewares, such a redux-promise, which seem to be fine if you know what the successive actions are at the point of the root (for lack of a better term) action being triggered.

基本上,我想维护一个可以随时添加的动作队列。每个对象在其状态中具有该队列的实例,并且相关的动作可以相应地排队,处理和出列。我有一个实现,但是这样做我在我的动作创建者中访问状态,这感觉就像一个反模式。

Essentially, I'd like to maintain a queue of actions that can be added to at any point. Each object has an instance of this queue in its state and dependent actions can be enqueued, processed and dequeued accordingly. I have an implementation, but in doing so I'm accessing state in my action creators, which feels like an anti-pattern.

我会尝试给出一些上下文用例和实现。

I'll try and give some context on use case and implementation.

假设您要创建一些列表并将其保留在服务器。在列表创建时,服务器使用该列表的id进行响应,该ID用于与列表相关的后续API端点:

Suppose you want to create some lists and persist them on a server. On list creation, the server responds with an id for that list, which is used in subsequent API end points pertaining to the list:

http://my.api.com/v1.0/lists/           // POST returns some id
http://my.api.com/v1.0/lists/<id>/items // API end points include id

想象一下,客户希望对这些API点执行乐观更新,增强用户体验 - 没有人喜欢看旋转器。因此,当您创建列表时,会立即显示新列表,并在添加项目时添加选项:

Imagine that the client wants to perform optimistic updates on these API points, to enhance UX - nobody likes looking at spinners. So when you create a list, your new list instantly appears, with an option at add items:

+-------------+----------+
|  List Name  | Actions  |
+-------------+----------+
| My New List | Add Item |
+-------------+----------+

假设有人在初始创建调用的响应之前尝试添加项目。 items API依赖于id,因此我们知道在拥有该数据之前我们无法调用它。但是,我们可能希望乐观地显示新项目并将对项目API的调用排入队列,以便在创建调用完成后触发它。

Suppose that someone attempts to add an item before the response from the initial create call has made it back. The items API is dependent on the id, so we know we can't call it until we have that data. However, we might want to optimistically show the new item and enqueue a call to the items API so that it triggers once the create call is done.

我目前用来解决这个问题的方法是给每个列表一个动作队列 - 也就是说,将连续触发的Redux动作列表。

The method I'm using to get around this currently is by giving each list an action queue - that is, a list of Redux actions that will be triggered in succession.

列表创建的reducer功能可能如下所示:

The reducer functionality for a list creation might look something like this:

case ADD_LIST:
  return {
    id: undefined, // To be filled on server response
    name: action.payload.name,
    actionQueue: []
  }

然后,在动作创建者中,我们将行动排队,而不是直接触发它:

Then, in an action creator, we'd enqueue an action instead of directly triggering it:

export const createListItem = (name) => {
    return (dispatch) => {
        dispatch(addList(name));  // Optimistic action
        dispatch(enqueueListAction(name, backendCreateListAction(name));
    }
}

为简洁起见,假设backendCreateListAction函数调用一个fetch API,它会在成功/失败时将消息从列表中发送出去。

For brevity, assume the backendCreateListAction function calls a fetch API, which dispatches messages to dequeue from the list on success/failure.

这里让我担心的是enqueueListAction方法的实现。这是我访问状态以控制队列进度的地方。它看起来像这样(忽略名称上的这种匹配 - 这实际上在实际中使用了clientId,但我试图保持示例简单):

What worries me here is the implementation of the enqueueListAction method. This is where I'm accessing state to govern the advancement of the queue. It looks something like this (ignore this matching on name - this actually uses a clientId in reality, but I'm trying to keep the example simple):

const enqueueListAction = (name, asyncAction) => {
    return (dispatch, getState) => {
        const state = getState();

        dispatch(enqueue(name, asyncAction));{

        const thisList = state.lists.find((l) => {
            return l.name == name;
        });

        // If there's nothing in the queue then process immediately
        if (thisList.actionQueue.length === 0) {
            asyncAction(dispatch);
        } 
    }
}

这里假设入队method返回一个普通操作,该操作将异步操作插入到列表actionQueue中。

Here, assume that the enqueue method returns a plain action that inserts an async action into the lists actionQueue.

整个事情感觉有点不利于我,但我不确定是否还有其他方法可以解决这个问题。另外,由于我需要在我的asyncActions中调度,我需要将调度方法传递给它们。

The whole thing feels a bit against the grain, but I'm not sure if there's another way to go with it. Additionally, since I need to dispatch in my asyncActions, I need to pass the dispatch method down to them.

该方法中有类似的代码从列表中出列,如果存在下一个动作则触发下一个动作:

There is similar code in the method to dequeue from the list, which triggers the next action should one exist:

const dequeueListAction = (name) => {
    return (dispatch, getState) => {
        dispatch(dequeue(name));

        const state = getState();
        const thisList = state.lists.find((l) => {
            return l.name === name;
        });

        // Process next action if exists.
        if (thisList.actionQueue.length > 0) {
            thisList.actionQueue[0].asyncAction(dispatch);
    }
}

一般来说,我可以忍受这个,但我我担心这是一种反模式,在Redux中可能会有更简洁,惯用的方式。

Generally speaking, I can live with this, but I'm concerned that it's an anti-pattern and there might be a more concise, idiomatic way of doing this in Redux.

任何帮助都表示赞赏。

推荐答案

我有一个完美的工具,可以满足您的需求。当你需要对redux进行大量控制时,(尤其是异步)你需要按顺序执行redux动作,没有比 Redux Sagas 。它建立在es6生成器之上,为您提供了很多控制,因为从某种意义上说,您可以在某些点暂停代码。

I have the perfect tool for what you are looking for. When you need a lot of control over redux, (especially anything asynchronous) and you need redux actions to happen sequentially there is no better tool than Redux Sagas. It is built on top of es6 generators giving you a lot of control since you can, in a sense, pause your code at certain points.

您描述的操作队列就是所谓的传奇。现在,因为它是为了与redux一起工作而创建的,所以可以通过在组件中调度来触发这些传闻。

The action queue you describe is what is called a saga. Now since it is created to work with redux these sagas can be triggered to run by dispatching in your components.

由于Sagas使用生成器,您还可以确定您的调度按特定顺序发生,仅在某些条件下发生。以下是他们的文档中的示例,我将引导您完成它以说明我的意思:

Since Sagas use generators you can also ensure with certainty that your dispatches occur in a specific order and only happen under certain conditions. Here is an example from their documentation and I will walk you through it to illustrate what I mean:

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    const token = yield call(authorize, user, password)
    if (token) {
      yield call(Api.storeItem, {token})
      yield take('LOGOUT')
      yield call(Api.clearItem, 'token')
    }
  }
}

好吧,起初看起来有些混乱,但这个传奇定义了登录序列需要发生的确切顺序。由于生成器的性质,允许无限循环。当您的代码进入 yield 时,它将停在该行并等待。在你告诉它之前,它不会继续下一行。所以看看它说 yield take('LOGIN_REQUEST')。该传奇将在此时产生或等待,直到你发送'LOGIN_REQUEST',之后saga将调用authorize方法,直到下一个yield。下一个方法是异步 yield调用(Api.storeItem,{token}),因此在代码解析之前它不会转到下一行。

Alright, it looks a little confusing at first but this saga defines the exact order a login sequence needs to happen. The infinite loop is allowed because of the nature of generators. When your code gets to a yield it will stop at that line and wait. It will not continue to the next line until you tell it to. So look where it says yield take('LOGIN_REQUEST'). The saga will yield or wait at this point until you dispatch 'LOGIN_REQUEST' after which the saga will call the authorize method, and go until the next yield. The next method is an asynchronous yield call(Api.storeItem, {token}) so it will not go to the next line until that code resolves.

现在,这就是魔术发生的地方。该传奇将再次停在 yield take('LOGOUT'),直到您在应用程序中发送LOGOUT。 这是至关重要的,因为如果你在LOGOUT之前再次发送LOGIN_REQUEST,则不会调用登录过程。现在,如果你发送LOGOUT,它将循环回到第一个yield并等待应用程序发送LOGIN_REQUEST再次。

Now, this is where the magic happens. The saga will stop again at yield take('LOGOUT') until you dispatch LOGOUT in your application. This is crucial since if you were to dispatch LOGIN_REQUEST again before LOGOUT, the login process would not be invoked. Now, if you dispatch LOGOUT it will loop back to the first yield and wait for the application to dispatch LOGIN_REQUEST again.

到目前为止,Redux Sagas是我最喜欢使用Redux的工具之一。它为您提供了对应用程序的大量控制,任何阅读代码的人都会感谢您,因为现在一切都在一行中读取。

Redux Sagas are, by far, one of my favorite tools to use with Redux. It gives you so much control over your application and anyone reading your code will thank you since everything now reads one line at a time.

这篇关于Redux中的排队操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆