re-frame: добавление нового элемента карты во вложенный вектор

У меня есть эта структура (упорядоченная карта) в моей «БД» с ключевым словом «: вопросы»:

{:33 {:question "one", :id 33, :answers [{:id 22, :question_id 33, :answer "one", :correct false}
                                              {:id 4,  :question_id 33, :answer "two", :correct false}]}},
{:39 {:question "two", :id 39, :answers []}},
{:41 {:question "three", :id 41, :answers [{:id 29, :question_id 41, :answer "one", :correct false}
                                                {:id 35, :question_id 41, :answer "two", :correct true}]}} 

Я могу добавить новый вопрос в обработчик события "re-frame/reg-event-db", добавив:

(assoc-in db [:questions (:id response)] new-map-stuff)

но я не знаю, как добавить карту в ключ ":answers". Кроме того, я боюсь, что я отображаю все вопросы каждый раз, когда добавляю новый ответ.

Я читал о перехватчике «пути» (своего рода «обновление»), но не могу найти пример того, как его использовать.


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


Ответы (1)


Вы делаете это так же, как в простом clojure, используя update-in. Первое знакомство с Clojure CheatSheet:

http://jafingerhut.github.io/cheatsheet/clojuredocs/cheatsheet-tiptip-cdocs-summary.html

или версия ClojureScript: http://cljs.info

Посмотрите документы для update-in: https://clojuredocs.org/clojure.core/update-in

Вам нужно 2 компонента

  1. Путь для навигации по сайту мутации
  2. Функция для выполнения мутации данной точки из (1).

Вы не полностью указали свои данные. Я предполагаю, что это выглядит так:

(def db {:questions [
           {:33 {:question "one", :id 33,
                 :answers  [{:id 22, :question_id 33, :answer "one", :correct false}
                            {:id  4, :question_id 33, :answer "two", :correct false}]}},
           {:39 {:question "two", :id 39, :answers []}},
           {:41 {:question "three", :id 41,
                 :answers  [{:id 29, :question_id 41, :answer "one", :correct false}
                            {:id 35, :question_id 41, :answer "two", :correct true}]}}]})

Я составлю новую карту answer, чтобы добавить:

(def new-answer {:id 42, :question_id 442, :answer "The Ultimate", :correct false})

Этот код показывает процесс по частям.

  (let [submap       (get-in db [:questions 0 :33 :answers])
        modified     (conj submap new-answer)
        final-answer (update-in db [:questions 0 :33 :answers] conj new-answer)   ]

Сначала навигация для получения submap:

submap => 
[{:id 22, :question_id 33, :answer "one", :correct false}
 {:id  4, :question_id 33, :answer "two", :correct false}]

И как вы добавляете новый ответ:

modified => 
[{:id 22, :question_id  33, :answer "one", :correct false}
 {:id  4, :question_id  33, :answer "two", :correct false}
 {:id 42, :question_id 442, :answer "The Ultimate", :correct false}]

И объединяем все это в одну операцию:

final-answer => 
{:questions
 [{:33
   {:question "one",
    :id 33,
    :answers
    [{:id 22, :question_id  33, :answer "one", :correct false}
     {:id  4, :question_id  33, :answer "two", :correct false}
     {:id 42, :question_id 442, :answer "The Ultimate", :correct false}]}}
  {:39 {:question "two", :id 39, :answers []}}
  {:41
   {:question "three",
    :id 41,
    :answers
    [{:id 29, :question_id 41, :answer "one", :correct false}
     {:id 35, :question_id 41, :answer "two", :correct true}]}}]}

Все это работает в ClojureScript так же, как и в Clojure.

Не беспокойтесь об использовании перехватчика path. Я думаю, что это больше сбивает с толку, чем помогает. И не беспокойтесь о скорости рендеринга, если:

  1. У вас это работает и проверено, и вы знаете, что это правильно.
  2. Вы измерили скорость рендеринга и можете задокументировать наличие проблемы и количество, которое вам нужно ускорить.
  3. Для длинных списков вещей посмотрите на метаданные key, которые вы можете добавить к каждому элементу списка для Reagent. См.: Reagent React Clojurescript Warning : каждый элемент последовательности должен иметь уникальный ключ :key

Обновить

Обратите внимание, что submap и modified показаны только в демонстрационных целях, чтобы показать, как работает update-in. Выражение update-in — это единственное, что вы действительно должны включить в свой код. Также обратите внимание, что update-in использует только db и new-answer.

person Alan Thompson    schedule 11.10.2018
comment
Большое спасибо! Просто заметьте, что в блоке dotest последним вызовом является: final-answer (update-in db [:questions 0 :33 :answers] conj изменено), верно? - person aarkerio; 11.10.2018
comment
Нет. Мы используем conj для добавления new-answer в конец существующего массива :answers. Исследование update и update-in. Путь, который находит modified, автоматически вставляет ссылку на пункт назначения в качестве первого аргумента функции conj. new-answer становится вторым аргументом для conj. Результат conj затем сшивается обратно на место глубоко в иерархии. - person Alan Thompson; 11.10.2018