Как сбросить счетчик в Re-frame (ClojureScript)

Это должно быть одна из тех глупых/сложных вещей, которые все обнаруживают при изучении нового фреймворка. Итак, у меня есть эта функция:

(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 не содержит никаких важных данных, это просто «визуальный» счетчик для отображения числа в списке. При первой загрузке страницы все работает нормально, если в списке отображается пять вопросов (1..5), проблема в том, что при создании/редактировании/удалении вопроса подписка:

 @(rf/subscribe [:questions])

снова вызывается, и тогда, конечно, отображается список, но теперь с 6 по 11. Поэтому мне нужен способ сбросить @counter.


person aarkerio    schedule 26.02.2020    source источник


Ответы (1)


Вы не должны использовать атом для этой цели. Ваш код должен выглядеть примерно так:

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

Функция tupelo.core/indexed из библиотеки Tupelo просто добавляет отсчитываемое от нуля значение индекса к каждому элементу в коллекции:

(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. Использование уникального идентификатора внутри элемента данных (т. е. идентификатора пользователя и т. д.) или просто хэш-кода обеспечивает уникальное справочное значение. Итак, на практике ваш код лучше написать так:

(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
person Alan Thompson    schedule 27.02.2020
comment
Если вам не нужна внешняя библиотека, в ядре clojure также есть map-indexed. - person Walton Hoops; 27.02.2020