如何在 re-frame 中按顺序运行处理程序?

3

从这两个处理程序开始,它们负责获取当前用户信息:

(re-frame/register-handler
  :got-user
  (fn [db [_ user]]
    (assoc db :user user)))

(re-frame/register-handler
  :get-user
  (fn [db [_]]
    (ajax/GET "/user" 
       {:handler #(re-frame/dispatch [:got-user %1])})
    db))

在一个页面中,我想显示朋友列表,但问题在于它取决于先获取用户:
(re-frame/register-handler
  :get-friends
  (fn [db [_]]
    (when (nil? (:user db))
      (re-frame/dispatch [:get-user]))
    ; Here's the problem, as I need to way for get-users and got-users to run.
    (ajax/GET (str "/users/" (get-in db [:user :id])) 
       {:handler #(re-frame/dispatch [:got-friends %1])})
    db))

我应该如何结构化这段代码?


我刚刚发现了re-frame,但是这里有一个想法:为什么不以一种继续传递风格分派[:get-user {:next :get-user-friends}],其中:next是当我们得到用户时要分派的事件? :got-user的处理程序将分派下一个事件。 因此,:get-friends被分成两个部分::get-friends(如果需要获取用户)和:get-user-friends(获取已知用户的朋友)。 - coredump
@coredump 是的,我曾经尝试过那个,但 continuations 很难维护和调试,并且在我看来,将关键字作为 continuation 传递在未来肯定不够用。 - pupeno
我不明白为什么它会很难维护和调试。在我看来,你已经在使用基于事件的回调(处理程序)方法。依我之见,事件链与现有框架融合得很好。如果我理解正确,您可以将任意参数传递给处理程序以满足未来需求,而不仅仅是关键字。干杯。 - coredump
@coredump 是的,一个事件驱动的系统本来就很难调试,但我们也没有太多办法。由于很难推断事情发生的原因,所以继续传递样式也很难调试。在运行代码X时,你没有追踪记录,只知道来自某个地方的运行代码X的继续。我曾经使用Scheme和Common Lisp中的继续Web服务器,它们让事情变得更加困难。 - pupeno
@coredump 另一个问题是现在处理程序还实现了连续性,这是两个不同的任务。这里缺乏关注点分离。想象一下10个处理程序都实现连续性,它们可能会以不同的方式实现,并且有些可能在某些情况下工作,而有些则不行。当然,我可以使用函数和宏来抽象连续部分,但由于这本质上将成为定义处理程序的新方法,因此在扩展它之前,我希望确保自己没有错过 re-frame 本身的任何内容。 - pupeno
阅读了更多的文档后,我认为我会尝试使用 https://github.com/Day8/re-frame#subscribe 解决你的问题。祝好运。 - coredump
2个回答

2

首先,文档明确警告不要创建带有副作用的处理程序(即从事件处理程序中分派事件)。其次,不要尝试重用事件处理程序,而是将可重用部分重构为辅助函数。使用re-frame-http-fx,我会编写以下代码:

(reg-event-fx
  :get-friends
  [trim-v]
  (fn
    [{db :db} [_]]
    {:http-xhrio  (if (:user db)
                  {:method          :get
                  :uri             (str "/users/" (get-in db [:user :id])) 
                  :on-success      [:got-friends]
                  :on-failure      [:get-friends-load-failed]}

                  {:method          :get
                  :uri             "/user" 
                  :on-success      [:get-friends-for-user]
                  :on-failure      [:get-friends-user-load-failed]})
     :db         db}
    ))

:get-friends-for-user会先存储检索到的用户,然后开始获取朋友信息。


我意识到回答你的问题已经晚了两年 :) 但是由于似乎没有一个好的解决方案,我认为我的答案可能会帮助其他看到你问题的人。 - Kalle

1
解决CPU占用问题的文章中得到一些启发,我让get-friends在用户为空时调用自身,但为了避免向服务器发送多次请求,它还会添加一个关于被请求的用户的标志。
(re-frame/register-handler
  :get-friends
  (fn [db [_ user-requested]]
    (if-let [user (:user db)]
      (ajax/GET (str "/users/" (get-in db [:user :id]))
                {:handler #(re-frame/dispatch [:got-friends %1])})
      (do
        (when (not user-requested)
          (re-frame/dispatch [:get-user]))
        (re-frame/dispatch [:get-friends true])))
    db))

它能够正常工作,但在本地机器上,直到got-user设置用户之前,它会调用自身9次左右。不确定这是否会成为问题,如果有其他解决方案,我也很乐意尝试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接