13

考虑以下 clojurescript 代码,其中使用了 spectre、reagent 和 re-frame 框架,外部 React.js 网格组件用作视图组件。

在 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}]})

在 subs.cls

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

选择结果:

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

在views.cls

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

上面的代码按预期工作。

我不想使用 react.js 组件显示数据,而是想遍历 :cats 向量中的每个映射并在 html ul / li 中显示 :text 项。

我开始如下:

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

预期输出:

ROOT
Objects
Version
X1

如何在重新框架中遍历订阅的集合并将数据显示为列表项?(=标题问题)。

4

3 回答 3

37

首先,明确你为什么使用key...

当列表非常动态时,为列表中的每个项目提供一个key非常有用 - 当定期添加和删除新列表项目时,特别是如果该列表很长,并且项目正在列表顶部附近添加/删除。

keys可以带来很大的性能提升,因为它们允许 React 更有效地重绘这些可变列表。或者,更准确地说,它允许 React 避免重新绘制与上次具有相同键、没有更改以及只是向上或向下移动的项目。

其次,如果列表非常静态(它不会一直更改)或者如果每个项目没有关联的唯一值,请明确您应该做什么......

根本不要使用:key。相反,into像这样使用:

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

注意这里发生了什么。提供的列表是map折叠向量。在它的最后,没有清单。只是嵌套向量。into[:ul]

当您将 a 嵌入list到 hiccup 中时,您只会收到有关丢失键的警告。上面没有嵌入list,只是vectors

第三,如果你的列表真的是动态的......

为每个项目添加一个唯一key的(唯一的兄弟姐妹)。在给出的示例中,它:text本身已经足够好了key(我假设它是独一无二的):

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

map将导致 alist这是[:ul]. 当 Reagent/React 看到list它希望keys在每个项目上看到时(请记住列表与 Reagent hiccup 中的向量不同)并将向控制台打印警告keys将丢失。

所以我们需要keylist. 在上面的代码中,我们没有:key通过元数据添加(尽管您可以根据需要这样做),而是key通过第一个参数(的[:li])提供,它通常也携带样式数据。

最后 - 第 1 部分不要map-indexed按照另一个答案中的建议使用。

key应该是与每个项目关联的唯一值。附加一些任意整数没有任何用处 - 好吧,它确实消除了控制台中的警告,但into如果这就是你想要的,你应该使用上面的技术。

最后-第2部分在这种情况下 map和之间没有区别for

它们都导致list. 如果那list有钥匙,那么没有警告。但是,如果缺少键,则会出现很多警告。但是列表是如何创建的并没有涉及。

所以,这个for版本与版本几乎相同map。有些人可能更喜欢它:

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

也可以使用如下元数据编写:

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

最后 - 第 3 部分

mapv由于这个问题是一个问题: https ://github.com/Day8/re-frame/wiki/Using-%5Bsquare-brackets%5D-instead-of-%28parentheses%29#appendix-2

于 2016-05-12T11:59:01.460 回答
3

编辑:有关键和的更连贯和技术上正确的解释map,请参阅Mike Thompson的答案!


我会这样写:

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

几点:

  1. 请参阅re-frame 自述文件的订阅部分,接近尾声,其中说:

    订阅只能在 Form-2 组件中使用,并且订阅必须在外部设置函数中,而不是在内部渲染函数中。所以下面是错误的(对比上面的正确版本)……</p>

    • 因此,您的组件是“错误的”,因为它没有将渲染器包装在内部函数中。自述文件包含所有细节,但简而言之,不将依赖于订阅的组件渲染器包装在内部函数中是不好的,因为这会导致组件在db更改时重新渲染——这不是你想要的!您希望组件仅在订阅更改时重新呈现。
  2. 编辑:说真的,请参阅Mike Thompson 的回答无论出于何种原因,我更喜欢使用map它来创建一系列 Hiccup 标签。您也可以使用for循环,但关键是每个[:li]Hiccup 向量都需要:key在其元数据中添加一个条目,我在此处使用@cats向量中的当前类别索引添加了该条目。如果你没有:key,React 将在开发控制台中抱怨。请注意,这个键应该以某种方式将这个元素唯一地绑定@cats到这个标签:如果cats订阅发生变化并被打乱,结果可能不是你所期望的,因为我只是使用了这个非常简单的键。如果您可以保证类别名称是唯一的,您可以只使用该:test值,或者:test价值,或者别的什么。关键是,键必须是唯一的,并且必须唯一地标识这个元素。
    • (注意:不要尝试使用Hiccup 标签mapv来制作矢量——re-frame 讨厌这样。必须与产生的seq东西相似map。)
  3. 我还举了一个例子main-panel来强调
    • 父组件不需要其子组件所需的订阅,并且
    • 您应该categorymanager2使用方括号而不是作为带有括号的函数调用组件(请参阅使用 [] 而不是 ())。
于 2016-05-11T19:30:56.420 回答
2

这是一个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)])))

我从这个重构教程中获取了该代码。

也比使用试剂时map更可取。for这有一个技术原因,只是我不知道它是什么。

于 2016-05-11T16:46:42.873 回答