2015-10-23 17 views
17

Trong một ứng dụng nhỏ tôi xây dựng có sử dụng thuốc thử và Re-frame Tôi đang sử dụng đa phương để gửi trang nào sẽ được hiển thị dựa trên một giá trị trong tình trạng ứng dụng:Tại sao đa phương pháp không hoạt động như chức năng cho Thuốc thử/Tái khung?

(defmulti pages :name) 

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

và sau đó tôi có các phương pháp như:

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

nơi register-page chức năng sẽ tạo ra cái nhìn thực tế:

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

tôi đã cố gắng changing my app so that the methods generated the pages directly, như trong:

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

và điều đó không làm cho trang không bao giờ được hiển thị. Trong bảng điều khiển chính của tôi, tôi changed the call to pages to square brackets nên thuốc thử mà có thể có tầm nhìn vào nó:

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

và khiến trang thăm đầu tiên để làm việc, nhưng sau đó, nhấp vào liên kết (gây vãng lộ trình để thay đổi) không có hiệu ứng.

Tất cả các không gian tên xác định các phương thức riêng lẻ được yêu cầu trong tệp được tải trước, chứa hàm init và thực tế là tôi có thể chọn bất kỳ trang nào và hiển thị mã chứng minh đang tải (sau đó, chuyển đổi đến một trang khác không hoạt động):

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

Trong một nỗ lực để gỡ lỗi những gì đang xảy ra, tôi đã xác định hai tuyến đường, :about:about2, một như một chức năng và một như một phương pháp:

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

và thực hiện bố cục in kết quả gọi pages (đã phải sử dụng cuộc gọi rõ ràng thay vì dấu ngoặc vuông của khóa học). Các chức năng bao bọc, một trong đó hoạt động, trả về:

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

trong khi phương thức trả về:

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

Nếu tôi thay đổi phương pháp là:

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

có nghĩa là, trả lại trong một vector, sau đó, nó bắt đầu hoạt động. Và nếu tôi thực hiện thay đổi ngược lại với chức năng bao bọc, nó bắt đầu thất bại trong việc theo cách tương tự như phương pháp:

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

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

Làm cho một chút cảm giác như cú pháp thuốc thử là [function] nhưng nó được cho là để gọi hàm tự động .

Tôi cũng bắt đầu xuất ra @current-route cho trình duyệt, như trong:

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

và tôi xác nhận @current-route đang được sửa đổi một cách chính xác và đầu ra được cập nhật, chỉ cần không [pages @current-route].

Mã nguồn đầy đủ cho ứng dụng của tôi có thể được tìm thấy ở đây: https://github.com/carouselapps/ninjatools/tree/multi-methods

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

+0

Bạn có yêu cầu không gian tên nơi bạn sử dụng 'defmethod' trong một số không gian tên gốc không? Bởi vì nếu bạn không yêu cầu các không gian tên đó một cách rõ ràng, các phương thức của bạn đơn giản là không được thêm vào multimethod. – skrat

+0

@skrat yes. Tôi sẽ chỉnh sửa câu hỏi để lưu ý điều đó. – Pablo

+0

Để gỡ lỗi này tôi sẽ 1. Thêm một phương pháp mặc định và 2. in ra những gì multimethod đang trở về (có lẽ là nil). – PeakCode

Trả lời

4

tôi không có tất cả các chi tiết, nhưng dường như, khi tôi đang render các trang như thế này:

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

Thuốc thử không nhận thấy rằng pages phụ thuộc vào giá trị của @current-route. Chrome React plugin đã giúp tôi tìm ra. Tôi đã cố gắng sử dụng một con chuột thay vì một thuê bao và dường như làm việc tốt. Rất may, telling Reagent/React the key to an element is easy enough:

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

Điều đó chỉ hoạt động tốt.

+0

thuốc thử/Phản ứng sẽ phá hủy và tạo lại thành phần trang mỗi lần thay đổi chính. Vì vậy, phương pháp này sẽ làm việc, nhưng chỉ cần nhận thức được làm thế nào. –

+0

Chỉ cần rõ ràng, tuyên bố này: 'Thuốc thử không nhận thấy rằng các trang phụ thuộc vào giá trị của @ current-route' là không chính xác. Có một cái nhìn vào câu trả lời của tôi cho một lời giải thích về những gì đang xảy ra. –

2

Vấn đề đầu tiên mà nhảy ra lúc tôi là phương pháp của bạn không có đối số:

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

Ở đây bạn có một danh sách trống rỗng, nhưng có lẽ bạn sẽ gọi hàm đa phương thức này với một hoặc hai đối số (vì hàm dispatch của nó là từ khóa và từ khóa c được gọi với một hoặc hai đối số).

Nếu bạn muốn gọi multimethod này với một đối số duy nhất và phớt lờ nó bên trong cơ thể của phương pháp :register, thay đổi ở trên để

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

Ngoài ra, tôi hy vọng bạn có thể sẽ muốn gọi số pages như bạn đã làm trước đó (nghĩa là, hoàn nguyên thay đổi thành dấu ngoặc vuông mà bạn đã đề cập trong câu hỏi).


Điều này có thể hoặc không thể khắc phục ứng dụng - có thể có các vấn đề khác - nhưng bạn nên bắt đầu. (Các multimethod chắc chắn sẽ không làm việc với những arglists trống nếu bạn vượt qua trong bất kỳ đối số.)

+0

Điểm tốt về danh sách đối số. Tôi nghĩ ClojureScript không kiểm tra tính chất của các hàm và phương thức. Tôi đã thực hiện thay đổi này nhưng ứng dụng vẫn hoạt động theo cùng một cách: https://github.com/carouselapps/ninjatools/commit/971de441b46e407857625d5c8f0b90fd78a54dd0 – Pablo

11

Vì vậy, một thành phần như sau: [pages @some-ratom]
sẽ lập lại khi pages thay đổi hoặc @some-ratom thay đổi.

Từ quan điểm của thuốc thử, pages không thay đổi kể từ lần trước, nó vẫn là cùng một phương pháp trước đây. Nhưng @some-ratom có thể thay đổi, để có thể kích hoạt một lần nữa.

Nhưng khi điều này xảy ra, nó sẽ được thực hiện bằng phiên bản được lưu trong bộ nhớ cache pages. Sau khi tất cả, nó không xuất hiện để thuốc thử rằng pages đã thay đổi. Nó vẫn là cùng một multimethod nó đã được trước đây.

Phiên bản cache của pages sẽ, tất nhiên, là phiên bản đầu tiên của pages được trả lại - phiên bản đầu tiên của mutlimethod và không phiên bản mới, chúng tôi mong đợi để xem sử dụng.

Thuốc thử thực hiện bộ nhớ đệm này vì bộ nhớ đệm phải xử lý các chức năng Form-2. Nó phải giữ hàm render trả về.

Bottom line: vì bộ nhớ đệm, multimethods sẽ không làm việc rất tốt, trừ khi bạn tìm thấy một cách để hoàn toàn thổi lên các thành phần và bắt đầu một lần nữa, đó là những gì các phương pháp hiện-top-bình chọn thực hiện:
^{:key @current-route} [pages @current-route]
Tất nhiên, thổi lên các thành phần và bắt đầu lại có thể có những tác động không mong muốn riêng của nó (tùy thuộc vào những gì nhà nước địa phương được tổ chức trong thành phần đó).

Bối cảnh mơ hồ liên quan:
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

Làm thế nào về nếu bạn thay vì có một wrapper pages-component chức năng mà là một chức năng thường xuyên có thể được lưu trữ bởi thuốc thử. Nó sẽ giống như sau:

(defn pages-component [state] 
    (layout/pages @state)) 
Các vấn đề liên quan