20

在我正在构建的一个使用 Reagent 和 Re-frame 的小型应用程序中,我使用多种方法来调度应该根据应用程序状态中的值显示哪个页面:

(defmulti pages :name)

(defn main-panel []
  (let [current-route (re-frame/subscribe [:current-route])]
    (fn []
      ;...
      (pages @current-route))))

然后我有如下方法:

(defmethod layout/pages :register [_] [register-page])

register-page函数将生成实际视图的位置:

(defn register-page []
  (let [registration-form (re-frame/subscribe [:registration-form])]
    (fn []
      [:div
       [:h1 "Register"]
       ;...
       ])))

我尝试更改我的应用程序,以便方法直接生成页面,如下所示:

(defmethod layout/pages :register [_]
  (let [registration-form (re-frame/subscribe [:registration-form])]
    (fn []
      [:div
       [:h1 "Register"]
       ;...
       ])))

这导致没有页面被渲染。在我的主面板中,我将调用更改pages为方括号,以便 Reagent 可以看到它:

(defn main-panel []
  (let [current-route (re-frame/subscribe [:current-route])]
    (fn []
      ;...
      [pages @current-route])))

这会导致第一个访问的页面正常工作,但在那之后,单击链接(导致当前路由更改)没有任何效果。

在首先加载的文件中需要定义各个方法的所有命名空间,该文件包含 init 函数,并且我可以选择任何单个页面并显示它的事实证明代码正在加载(然后,切换到另一个页面不会不工作):

https://github.com/carouselapps/ninjatools/blob/master/src/cljs/ninjatools/core.cljs#L8-L12

为了调试正在发生的事情,我定义了两条路线,:about并且:about2,一条作为函数,一条作为方法:

(defn about-page []
  (fn []
    [:div "This is the About Page."]))

(defmethod layout/pages :about [_]
  [about-page])

(defmethod layout/pages :about2 [_]
  (fn []
    [:div "This is the About 2 Page."]))

并使布局打印调用的结果pages(当然必须使用显式调用而不是方括号)。包装的函数,一个有效的函数,返回:

[#object[ninjatools$pages$about_page "function ninjatools$pages$about_page(){
return (function (){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"div","div",1057191632),"This is the About Page."], null);
});
}"]]

而方法返回:

#object[Function "function (){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"div","div",1057191632),"This is the About 2 Page."], null);
}"]

如果我将方法更改为:

(defmethod layout/pages :about2 [_]
  [(fn []
     [:div "This is the About 2 Page."])])

也就是说,在向量中返回函数,然后它开始工作。如果我对包装函数进行反向更改,它会以与方法相同的方式开始失败:

(defn about-page []
  (fn []
    [:div "This is the About Page."]))

(defmethod layout/pages :about [_]
  about-page)

像 Reagent 的语法一样有点道理,[function]但它应该自动调用该函数。

我也开始输出@current-route到浏览器,如下所示:

[:main.container
 [alerts/view]
 [pages @current-route]
 [:div (pr-str @current-route)]]

并且我验证@current-route正在正确修改并且输出更新,只是没有[pages @current-route]

我的应用程序的完整源代码可以在这里找到:https ://github.com/carouselapps/ninjatools/tree/multi-methods

更新:根据 Michał Marczyk 的回答更正了方法的多样性。

4

4 回答 4

15

因此,像这样的组件:[pages @some-ratom]
将在pages更改或@some-ratom更改时重新呈现。

从试剂的角度来看,pages和上次没有变化,还是和之前一样的多方法。但@some-ratom可能会发生变化,因此可能会触发重新渲染。

但是当这种重新渲染发生时,它将使用缓存版本的pages. 毕竟,它似乎并没有pages改变试剂。它仍然是以前的多方法。

当然,缓存的版本pages将是第一个pages被渲染的版本——多方法的第一个版本,而不是我们期望看到的新版本。

Reagent 执行此缓存是因为它必须处理 Form-2 函数。它必须保留返回的渲染函数。

底线:由于缓存,多方法不会很好地工作,除非您找到一种方法来完全炸毁组件并重新开始,这就是当前投票最多的方法所做的:
^{:key @current-route} [pages @current-route]
当然,炸毁组件重新开始可能有其不受欢迎的影响(取决于该组件中的本地状态)。

模糊的相关背景:
https ://github.com/Day8/re-frame/wiki/Creating-Reagent-Components#appendix-a---lifting-the-lid-slightly
https://github.com/Day8/re -frame/wiki/When-do-components-update%3F

于 2015-11-02T21:53:52.297 回答
5

我没有所有细节,但显然,当我渲染这样的页面时:

[:main.container
 [alerts/view]
 [pages @current-route]]

试剂未能注意到pages取决于 的值@current-routeChrome React 插件帮助我解决了这个问题。我尝试使用随机数而不是订阅,这似乎工作正常。值得庆幸的是,告诉 Reagent/React 元素的关键很容易

[:main.container
 [alerts/view]
 ^{:key @current-route} [pages @current-route]]

这工作得很好。

于 2015-11-01T10:44:57.807 回答
2

我跳出来的第一个问题是你的方法没有参数:

(defmethod layout/pages :register [] [register-page])
                                  ^ arglist

这里你有一个空的 arglist,但大概你会用一个或两个参数调用这个多方法(因为它的调度函数是一个关键字,而关键字可以用一个或两个参数调用)。

如果你想用一个参数调用这个多方法并在:register方法体中忽略它,将上面的更改为

(defmethod layout/pages :register [_] [register-page])
                                   ^ argument to be ignored

另外,我希望您可能想像pages以前一样称呼自己(也就是说,将更改恢复为您在问题中提到的方括号)。


这可能会或可能不会修复应用程序 - 可能还有其他问题 - 但它应该让你开始。(如果您传入任何参数,多方法肯定不适用于那些空参数列表。)

于 2015-10-31T16:21:56.787 回答
1

如果你有一个包装pages-component函数,它是一个可以被试剂缓存的常规函数​​,怎么样。它看起来像这样:

(defn pages-component [state]
  (layout/pages @state))
于 2017-01-11T19:56:16.950 回答