clojure clojurescript reagent re-frame

clojure - ¿Por qué los métodos múltiples no funcionan como funciones para Reactivo/Re-encuadre?



clojurescript reagent (4)

¿Qué tal si, en cambio, tiene una función de pages-component envoltura que es una función regular que puede ser almacenada en caché por el reactivo? Se vería así:

(defn pages-component [state] (layout/pages @state))

En una aplicación pequeña que estoy creando, que utiliza Reactivos y Reencuadre, estoy usando métodos múltiples para enviar qué página se debe mostrar en función de un valor en el estado de la aplicación:

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

Y luego tengo métodos como:

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

donde la función de register-page generaría la vista real:

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

Intenté cambiar mi aplicación para que los métodos generaran las páginas directamente , como en:

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

y eso causó que ninguna página sea renderizada. En mi panel principal, cambié la llamada a las pages a los corchetes para que el reactivo tuviera visibilidad:

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

y eso hizo que la primera página visitada funcionara, pero después de eso, hacer clic en los enlaces (lo que hace que la ruta actual cambie) no tiene efecto.

Todos los espacios de nombres que definen los métodos individuales se requieren en el archivo que se carga primero, que contiene la función de inicio, y el hecho de que pueda seleccionar cualquier página y mostrarla demuestra que el código se está cargando (entonces, cambiar a otra página no lo hace). no funciona):

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

En un esfuerzo por depurar lo que está pasando, definí dos rutas :about2 :about y :about2 , una como función y la otra como método:

(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."]))

e hizo que el diseño imprimiera el resultado de las pages de llamada (tenía que usar la llamada explícita en lugar de los corchetes, por supuesto). La función envuelta, la que funciona, devuelve:

[#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); }); }"]]

mientras el método retorna:

#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); }"]

Si cambio el método a ser:

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

es decir, devolviendo la función en un vector, entonces, comienza a funcionar. Y si hago el cambio inverso a la función ajustada, comienza a fallar de la misma manera que el método:

(defn about-page [] (fn [] [:div "This is the About Page."])) (defmethod layout/pages :about [_] about-page)

Tiene un poco de sentido, ya que la sintaxis del reactivo es [function] pero se suponía que llamaba a la función automáticamente.

También comencé a enviar @current-route al navegador, como en:

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

y verifiqué @current-route se está modificando correctamente y la salida se actualizó, simplemente no [pages @current-route] .

El código fuente completo de mi aplicación se puede encontrar aquí: https://github.com/carouselapps/ninjatools/tree/multi-methods

Actualización: corrigió la aridad de los métodos siguiendo la respuesta de Michał Marczyk.


El primer problema que me llama la atención es que sus métodos no tienen argumentos:

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

Aquí tiene un arglista vacío, pero presumiblemente estará llamando a este método múltiple con uno o dos argumentos (ya que su función de envío es una palabra clave y las palabras clave se pueden llamar con uno o dos argumentos).

Si desea llamar a este método múltiple con un solo argumento y simplemente ignorarlo dentro del cuerpo del método de :register , cambie lo anterior a

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

Además, creo que probablemente querrá llamar a las pages como lo hizo anteriormente (es decir, revertir el cambio a los corchetes que mencionó en la pregunta).

Esto puede o no arreglar la aplicación (puede haber otros problemas), pero debería comenzar. (El método múltiple definitivamente no funcionará con esos arglistas vacíos si pasas cualquier argumento).


Entonces, un componente como este: [pages @some-ratom]
se redireccionará cuando cambien las pages o @some-ratom cambio de @some-ratom .

Desde el punto de vista del reactivo, las pages no han cambiado desde la última vez, sigue siendo el mismo método múltiple que era antes. Pero @some-ratom podría cambiar, por lo que podría desencadenar un cambio de dirección.

Pero cuando este reenvío ocurra, se hará usando una versión de pages caché. Después de todo, no parece reactivo que las pages hayan cambiado. Sigue siendo el mismo multimetodo que era antes.

La versión en caché de las pages será, por supuesto, la primera versión de las pages que se procesaron, la primera versión del método mutlim y no la nueva versión que esperamos que se use.

El reactivo realiza este almacenamiento en caché porque debe manejar las funciones de Form-2. Tiene que mantener la función de render devuelta.

En pocas palabras: debido al almacenamiento en caché, los métodos múltiples no funcionarán muy bien, a menos que encuentre la manera de hacer explotar completamente el componente y comenzar de nuevo, que es lo que hace el enfoque actualmente más votado:
^{:key @current-route} [pages @current-route]
Por supuesto, explotar el componente y comenzar de nuevo puede tener sus propias implicaciones no deseadas (dependiendo de qué estado local se encuentre en ese componente).

Antecedentes vagamente relacionados:
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


No tengo todos los detalles, pero al parecer, cuando estaba renderizando páginas como esta:

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

El reactivo no notó que las pages dependían del valor de @current-route . El plugin Chrome React me ayudó a resolverlo. Intenté usar un ratom en lugar de una suscripción y eso parecía funcionar bien. Afortunadamente, decirle a Reagent / React la clave para un elemento es bastante fácil :

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

Eso funciona bien.