2016-05-11 14 views
5

Considerare il seguente codice clojurescript in cui vengono utilizzati lo spettro, il reagente e i framework di re-frame, un componente di griglia React.js esterno viene utilizzato come componente di visualizzazione.Come faccio a passare da una collezione sottoscritta a una nuova cornice e visualizzo i dati come una lista?

In db.cls:

(def default-db 
    {:cats [{:id 0 :data {:text "ROOT" :test 17} :prev nil :par nil} 
      {:id 1 :data {:text "Objects" :test 27} :prev nil :par 0} 
      {:id 2 :data {:text "Version" :test 37} :prev nil :par 1} 
      {:id 3 :data {:text "X1" :test 47} :prev nil :par 2}]}) 

In subs.cls

(register-sub 
    :cats 
    (fn [db] 
    (reaction 
     (select [ALL :data] (t/tree-visitor (get @db :cats)))))) 

risultato da selezionare:

[{:text "ROOT", :test 17} 
{:text "Objects", :test 27} 
{:text "Version", :test 37} 
{:text "X1", :test 47}] 

In views.cls

(defn categorymanager [] 
     (let [cats (re-frame/subscribe [:cats])] 
     [:> Reactable.Table 
      {:data (clj->js @cats)}])) 

Il codice sopra funziona come previsto.

Invece di visualizzare i dati con il componente react.js, voglio passare attraverso ciascuna delle mappe nel vettore: cats e visualizzare gli elementi di testo in html ul/li.

ho iniziato come segue:

(defn categorymanager2 [] 
     (let [cats (re-frame/subscribe [:cats])] 
     [:div 
      [:ul 
      (for [category @cats] 
;;--- How to continue here ?? --- 
     ) 
     )) 

uscita prevista:

ROOT 
Objects 
Version 
X1 

Come faccio a ciclo attraverso una collezione sottoscritto nel ri-frame e visualizzare i dati come una lista-articolo? (= domanda per titolo).

risposta

20

primo, essere chiaro il motivo per cui si utilizza key ...

Fornire un key per ogni elemento in un elenco è utile quando tale elenco è abbastanza dinamica - quando nuove voci della lista vengono aggiunti e rimossi regolarmente, soprattutto se l'elenco è lungo e gli elementi vengono aggiunti/rimossi nella parte superiore dell'elenco.

keys in grado di fornire grandi miglioramenti delle prestazioni, perché consentono a React di ridisegnare in modo più efficiente questi elenchi modificabili. O, più precisamente, consente a React di evitare di ridisegnare elementi che hanno la stessa chiave dell'ultima volta, e che non sono stati modificati, e che sono stati semplicemente mescolati in alto o in basso.

In secondo luogo, essere ciò che si dovrebbe fare chiaro se la lista è piuttosto statica (non cambia tutto il tempo) o se non v'è alcun valore univoco associato a ogni voce ...

Don usare il :key affatto. Invece, utilizzare into come questo:

(defn categorymanager [] 
    (let [cats (re-frame/subscribe [:cats])] 
    (fn [] 
     [:div 
     (into [:ul] (map #(vector :li (:text %)) @cats))]))) 

Avviso quanto è successo qui.L'elenco fornito da map è piegato into vettore [:ul]. Alla fine, nessuna lista in vista. Solo vettori annidati.

Viene visualizzato solo un avviso relativo alla mancanza di chiavi quando si incorpora un errore list. Sopra non è incluso lo list, solo vectors.

Terzo, se l'elenco è davvero dinamica ...

una possibilità unica key a ciascun elemento (fratelli amoung unici). Nell'esempio riportato, il :text è di per sé una buona abbastanza key (suppongo che è unico):

(defn categorymanager [] 
    (let [cats (re-frame/subscribe [:cats])] 
    (fn [] 
     [:div 
     [:ul (map #(vector :li {:key (:text %)} (:text %)) @cats)]]))) 

Quello map si tradurrà in un list che è il primo parametro al [:ul]. Quando Reagent/React vede che list vorrà vedere keys su ciascun elemento (ricorda che gli elenchi sono diversi dai vettori in singhiozzo di Reagent) e stamperà gli avvisi alla console che erano keys mancanti.

Quindi è necessario aggiungere uno key a ciascun articolo dello list. Nel codice sopra non aggiungiamo :key tramite metadati (sebbene tu possa farlo in questo modo se vuoi), e invece stiamo fornendo il key tramite il 1 ° parametro (dello [:li]), che normalmente contiene anche dati di stile.

Infine - parte 1 NON usare map-indexed come suggerito in un'altra risposta.

key deve essere un valore univoco associato a ciascun articolo. Allegare un intero arb non fa nulla di utile - beh, si sbarazza degli avvertimenti nella console, ma dovresti usare la tecnica into qui sopra se è tutto ciò che vuoi.

Infine - parte 2 non v'è alcuna differenza tra map e for in questo contesto.

Entrambi producono un list. Se questo list ha le chiavi, allora nessun avvertimento. Ma se mancano le chiavi, allora molti avvertimenti. Ma come è stata creata la lista non ci entra.

Quindi, questa versione for è praticamente la stessa della versione map. Alcuni potrebbero preferire esso:

(defn categorymanager [] 
    (let [cats (re-frame/subscribe [:cats])] 
    (fn [] 
     [:div 
     [:ul (for [i @cats] [:li {:key (:text i)} (:text i)])]]))) 

che può anche essere scritta utilizzando i metadati come questo:

(defn categorymanager [] 
    (let [cats (re-frame/subscribe [:cats])] 
    (fn [] 
     [:div 
     [:ul (for [i @cats] ^{:key (:text i)}[:li (:text i)])]]))) 

Infine - parte 3

mapv è un problema a causa di questo problema: https://github.com/Day8/re-frame/wiki/Using-%5Bsquare-brackets%5D-instead-of-%28parentheses%29#appendix-2

+1

Perle dal creatore del re-frame! Grazie!!! –

+0

In reddit la tua risposta è stata descritta come epica. :-) –

1

Ecco un esempio ul/li:

(defn phone-component 
    [phone] 
    [:li 
    [:span (:name @phone)] 
    [:p (:snippet @phone)]]) 

(defn phones-component 
    [] 
    (let [phones (re-frame/subscribe [:phones])] ; subscribe to the phones value in our db 
    (fn [] 
     [:ul (for [phone in @phones] ^{:key phone} [phone-component phone] @phones)]))) 

ho afferrato che il codice da this reframe tutorial.

È inoltre preferibilea for quando si utilizza il reagente. C'è una ragione tecnica per questo, è solo che non so cosa sia.

2

Modifica: per una spiegazione dei tasti molto più coerente e tecnicamente corretta e map, vedere la risposta di Mike Thompson!


Ecco come avrei scritto che:

(defn categorymanager2 [] 
    (let [cats (re-frame/subscribe [:cats])] 
    (fn [] 
     [:div 
     [:ul 
     (map-indexed (fn [n cat] ;;; !!! See https://stackoverflow.com/a/37186230/500207 !!! 
         ^{:key n} 
         [:li (:text cat)]) 
        @cats)]]))) 
(defn main-panel [] 
    [:div 
    [categorymanager2]]) 

alcuni punti:

  1. Vedere la re-frame readme's Subscribe section, verso la fine, che dice:

    abbonamenti può essere utilizzato solo in componenti Form-2 e l'abbonamento mu essere nella funzione di configurazione esterna e non nella funzione di rendering interna. Così il seguente è sbagliato (confronta con la versione corretta sopra) ...

    • Pertanto, il componente era ‘sbagliato’ perché non avvolgere il renderer all'interno di una funzione interna.Il readme ha tutti i dettagli, ma in breve, non il wrapping di un renderer componente che dipende da una sottoscrizione all'interno di una funzione interna è errato perché ciò causa il rerender del componente ogni volta che cambia db, non quello che vuoi! Si desidera che il componente esegua nuovamente il rerender quando cambia la sottoscrizione.
  2. Modifica: seriamente, vedere Mike Thompson's answer. Per qualsiasi ragione, preferisco usare map per creare un seq di tag Hiccup. È anche possibile utilizzare un ciclo for, ma il punto critico è che ogni vettore di Singolo [:li] ha bisogno di una voce :key nei suoi metadati, che aggiungo qui utilizzando l'indice della categoria corrente nel vettore @cats. Se non si dispone di uno :key, React si lamenterà nella Dev Console. Nota che questa chiave dovrebbe in qualche modo legare univocamente questo elemento di @cats a questo tag: se l'abbonamento cats cambia e viene mescolato, il risultato potrebbe non essere quello che ti aspetti perché ho appena usato questa chiave molto semplice. Se è possibile garantire che i nomi delle categorie siano univoci, è sufficiente utilizzare il valore :test o il valore :test o qualcos'altro. Il punto è che la chiave deve essere unica e deve identificare univocamente questo elemento.
    • (NB: non cercare di utilizzare mapv per fare un vettore di Hiccup tag-re-telaio odia che deve essere una seq come quello che produce map..)
  3. Ho incluso anche un esempio main-panel sottolineare che
    • componenti genitore non hanno bisogno gli abbonamenti che i loro bambini hanno bisogno di componente, e che
    • 012.
    • è necessario chiamare il componente categorymanager2 con parentesi quadre anziché come funzione con Parens (vedere Using [] instead of()).
+0

Questa risposta ha b molto utile. Grazie. –

Problemi correlati