В небольшом приложении, которое я создаю для использования 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
на квадратные скобки, чтобы Реагент мог видеть его:
(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.