2015-10-23 5 views
17

W małej aplikacji Jestem budynku, który używa odczynników i Re-rama Używam multi-metody wysyłką która strona powinna być wyświetlane na podstawie wartości w stanie aplikacji:Dlaczego multi-metody nie działają jako funkcje dla odczynnika/ponownej ramki?

(defmulti pages :name) 

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

a potem mają metody takie jak:

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

gdzie funkcja register-page wygeneruje rzeczywisty widok:

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

próbowałem changing my app so that the methods generated the pages directly, jak w:

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

i to spowodowało, że żadna strona nie zostanie wyświetlona. Moim głównym panelu I changed the call to pages to square brackets tak, że odczynnik będzie miał wgląd w to:

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

i który spowodował pierwszy odwiedził stronę do pracy, ale po tym, klikając na linki (co powoduje, że prąd drogę do zmiany) nie ma efekt.

Wszystkie obszary nazw definiujące poszczególne metody są wymagane w pliku, który jest załadowany jako pierwszy, zawiera funkcję init, a fakt, że mogę wybrać jedną stronę i wyświetlić ją, dowodzi, że kod ładuje się (następnie przełączanie aby inna strona nie działa):

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

W celu debugowania, co się dzieje, ja zdefiniowane dwie trasy, :about i :about2, jeden jako funkcję i jedną jako metoda:

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

i utworzyłem układ wydruku wyniku wywołania pages (musiał użyć połączenia jawnego zamiast nawiasów kwadratowych oczywiście). Zawinięty funkcja, ten, który działa, zwraca:

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

natomiast metoda zwraca:

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

Gdybym zmienić metodę być:

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

czyli zawracania funkcja w wektorze, a następnie zaczyna działać. A jeśli robię odwrotną zmianę funkcji zapakowane, zaczyna się nie w ten sam sposób, co w sposobie:

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

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

sprawia trochę sensie jak składnia odczynnik jest [function] ale miało to automatycznie wywołać funkcję .

zacząłem też wyprowadzanie @current-route do przeglądarki, na przykład:

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

i zweryfikowane @current-route jest modyfikowany poprawnie i wyjście aktualizowane, po prostu nie [pages @current-route].

pełny kod źródłowy dla mojej aplikacji można znaleźć tutaj: https://github.com/carouselapps/ninjatools/tree/multi-methods

Update: corrected the arity of the methods following Michał Marczyk's answer.

+0

Czy potrzebujesz przestrzeni nazw, w której używasz 'defmethod' w jakiejś root przestrzeni nazw? Ponieważ, jeśli nie potrzebujesz jawnie tych przestrzeni nazw, twoje metody po prostu nie zostaną dodane do multimetra. – skrat

+0

@skrat tak. Będę edytować pytanie, aby to zauważyć. – Pablo

+0

Do debugowania tego chciałbym 1. Dodaj domyślną metodę i 2. wydrukować, co multimethod powraca (prawdopodobnie zero). – PeakCode

Odpowiedz

4

nie mam wszystkich szczegółów, ale jak widać, kiedy renderowania stron tak:

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

Odczynnik nie zauważył, że pages zależało od wartości @current-route. Chrome React plugin pomógł mi to rozgryźć. Próbowałem użyć ratom zamiast subskrypcji i wydawało mi się, że to działa dobrze. Na szczęście, telling Reagent/React the key to an element is easy enough:

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

To działa dobrze.

+0

odczynnik/React zniszczy i odtworzy komponent stron po każdym zmianie klucza. Ta metoda zadziała, ale bądź świadomy, jak to zrobić. –

+0

Dla jasności, to stwierdzenie: "Odczynnik nie zauważył, że strony zależą od wartości @ aktualnej trasy" jest niepoprawny. Spójrz na moją odpowiedź, aby wyjaśnić, co się dzieje. –

2

Pierwszym problemem, który wyskakuje na mnie jest to, że metody podjąć żadnych argumentów:

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

Tutaj masz pustego argumentu, ale prawdopodobnie nazwiesz ten multimetr z jednym lub dwoma argumentami (ponieważ jego funkcja wysyłania jest słowem kluczowym i słowami kluczowymi c być wywołanym z jednym lub dwoma argumentami).

Jeśli chcesz nazwać multimethod z jednym argumentem i zignoruj ​​go wewnątrz ciała metodą :register zmień wyżej

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

Również oczekuję prawdopodobnie będziesz chciał zadzwonić pod numer pages samemu, tak jak wcześniej (tzn. przywrócić zmianę w nawiasach kwadratowych wspomnianych w pytaniu).


To może, ale nie musi, naprawiać aplikację - mogą być inne problemy - ale powinno zacząć. (The multimethod pewno nie działać z tymi pustymi arglists jeśli przejdą w jakichkolwiek argumentów.)

+0

Dobra uwaga na temat listy argumentów. Myślę, że ClojureScript po prostu nie sprawdza arytmu funkcji i metod. Wprowadziłem tę zmianę, ale aplikacja nadal zachowuje się w taki sam sposób: https://github.com/carouselapps/ninjatools/commit/971de441b46e407857625d5c8f0b90fd78a54dd0 – Pablo

11

Więc składnikiem tak: [pages @some-ratom]
będzie rerender gdy pages zmiany lub @some-ratom zmiany.

Z punktu widzenia odczynnika, pages nie zmieniła się od ostatniego razu, wciąż jest to ta sama metoda wielokrotna, co wcześniej. Ale może zmienić się @some-ratom, co może spowodować wyrejestrowanie.

Ale, kiedy to się wydarzy, zostanie to zrobione przy użyciu buforowanej wersji pages. W końcu nie wydaje się, aby odczynnik zmienił się pages. Jest to wciąż ten sam multimetr, jaki był wcześniej.

zapisana w pamięci podręcznej pages będzie, oczywiście, być pierwszą wersję pages który został wydany - pierwszą wersję mutlimethod i nie nowej wersji możemy spodziewać się stosować.

Reagent wykonuje to buforowanie, ponieważ musi obsługiwać funkcje Form-2. Musi zachować zwróconą funkcję renderowania.

Konkluzja: ze względu na buforowanie, multimethods nie będzie działać bardzo dobrze, chyba, że ​​znajdzie sposób, aby całkowicie wysadzenia komponent i zacząć od nowa, co jest, co aktualnie podejście top-głosowało robi:
^{:key @current-route} [pages @current-route]
Oczywiście wysadzenie w powietrze komponentu i ponowne uruchomienie może mieć swoje niepożądane implikacje (w zależności od tego, jaki lokalny stan jest utrzymywany w tym komponencie).

Niejasno pokrewne Tło:
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

0

Jak około jeśli zamiast mieć otoki pages-component funkcję, która jest regularne funkcji, które mogą być buforowane przez odczynnika. Wyglądałoby to tak:

(defn pages-component [state] 
    (layout/pages @state)) 
Powiązane problemy