如何在 Re-frame (ClojureScript) 中重置计数器
How to reset a counter in Re-frame (ClojureScript)
这一定是每个人在学习新框架时都会发现的 silly/complex 事情之一。所以我有这个功能:
(defn display-questions-list
[]
(let [counter (atom 1)]
[:div
(doall (for [question @(rf/subscribe [:questions])]
^{:key (swap! counter inc)} [question-item (assoc question :counter @counter)])])))
@counter 原子不保存任何重要数据,它只是一个 "visual" 计数器来显示列表中的数字。第一次加载页面时,一切正常,如果列表显示五个问题 (1..5),问题是当问题是 created/edited/deleted 时,订阅:
@(rf/subscribe [:questions])
再次调用,然后当然显示列表,但现在从 6 到 11。所以我需要一种方法来重置 @counter。
您不应该为此目的使用原子。您的代码应该更像这样:
(ns tst.demo.core
(:use tupelo.test)
(:require [tupelo.core :as t]))
(defn display-questions-list
[]
[:div
(let [questions @(rf/subscribe [:questions])]
(doall (for [[idx question] (t/indexed questions)]
^{:key idx}
[question-item (assoc question :counter idx) ])))])
从 the Tupelo library simply prepends a zero-based index value 到集合中每个项目的 tupelo.core/indexed
函数:
(t/indexed [:a :b :c :d :e]) =>
([0 :a]
[1 :b]
[2 :c]
[3 :d]
[4 :e])
源代码非常简单:
(defn zip-lazy
"Usage: (zip-lazy coll1 coll2 ...)
(zip-lazy xs ys zs) => [ [x0 y0 z0]
[x1 y1 z1]
[x2 y2 z2]
... ]
Returns a lazy result. Will truncate to the length of the shortest collection.
A convenience wrapper for `(map vector coll1 coll2 ...)`. "
[& colls]
(assert #(every? sequential? colls))
(apply map vector colls))
(defn indexed
"Given one or more collections, returns a sequence of indexed tuples from the collections:
(indexed xs ys zs) -> [ [0 x0 y0 z0]
[1 x1 y1 z1]
[2 x2 y2 z2]
... ] "
[& colls]
(apply zip-lazy (range) colls))
更新
实际上,:key
元数据的主要目标是为列表。由于项目可能处于不同的顺序,因此使用列表索引值实际上是一种 React antipattern。使用来自数据元素(即用户 ID 等)或仅哈希码的唯一 ID 可提供唯一的参考值。所以,实际上你的代码最好写成这样:
(defn display-questions-list
[]
[:div
(doall (for [question @(rf/subscribe [:questions])]
^{:key (hash question)}
[question-item (assoc question :counter idx)]))])
一些哈希码示例:
(hash 1) => 1392991556
(hash :a) => -2123407586
(hash {:a 1, :b [2 3 4]}) => 383153859
这一定是每个人在学习新框架时都会发现的 silly/complex 事情之一。所以我有这个功能:
(defn display-questions-list
[]
(let [counter (atom 1)]
[:div
(doall (for [question @(rf/subscribe [:questions])]
^{:key (swap! counter inc)} [question-item (assoc question :counter @counter)])])))
@counter 原子不保存任何重要数据,它只是一个 "visual" 计数器来显示列表中的数字。第一次加载页面时,一切正常,如果列表显示五个问题 (1..5),问题是当问题是 created/edited/deleted 时,订阅:
@(rf/subscribe [:questions])
再次调用,然后当然显示列表,但现在从 6 到 11。所以我需要一种方法来重置 @counter。
您不应该为此目的使用原子。您的代码应该更像这样:
(ns tst.demo.core
(:use tupelo.test)
(:require [tupelo.core :as t]))
(defn display-questions-list
[]
[:div
(let [questions @(rf/subscribe [:questions])]
(doall (for [[idx question] (t/indexed questions)]
^{:key idx}
[question-item (assoc question :counter idx) ])))])
从 the Tupelo library simply prepends a zero-based index value 到集合中每个项目的 tupelo.core/indexed
函数:
(t/indexed [:a :b :c :d :e]) =>
([0 :a]
[1 :b]
[2 :c]
[3 :d]
[4 :e])
源代码非常简单:
(defn zip-lazy
"Usage: (zip-lazy coll1 coll2 ...)
(zip-lazy xs ys zs) => [ [x0 y0 z0]
[x1 y1 z1]
[x2 y2 z2]
... ]
Returns a lazy result. Will truncate to the length of the shortest collection.
A convenience wrapper for `(map vector coll1 coll2 ...)`. "
[& colls]
(assert #(every? sequential? colls))
(apply map vector colls))
(defn indexed
"Given one or more collections, returns a sequence of indexed tuples from the collections:
(indexed xs ys zs) -> [ [0 x0 y0 z0]
[1 x1 y1 z1]
[2 x2 y2 z2]
... ] "
[& colls]
(apply zip-lazy (range) colls))
更新
实际上,:key
元数据的主要目标是为列表。由于项目可能处于不同的顺序,因此使用列表索引值实际上是一种 React antipattern。使用来自数据元素(即用户 ID 等)或仅哈希码的唯一 ID 可提供唯一的参考值。所以,实际上你的代码最好写成这样:
(defn display-questions-list
[]
[:div
(doall (for [question @(rf/subscribe [:questions])]
^{:key (hash question)}
[question-item (assoc question :counter idx)]))])
一些哈希码示例:
(hash 1) => 1392991556
(hash :a) => -2123407586
(hash {:a 1, :b [2 3 4]}) => 383153859