#clojure #reagent #re-frame
#clojure #реагент #повторный кадр
Вопрос:
У меня есть пара вариантов, но оба кажутся немного запаздывающими, и я думаю, что должна быть лучшая альтернатива. Я просто хотел бы иметь возможность создавать формы, даже динамически создавать их (например, добавлять строки в форму из моего приложения), и иметь соответствующий реагенту / повторному кадру / реагированию доступ к значениям различных входных данных.
Не уверен, что любой из них является лучшей альтернативой, поскольку они оба запускают функции после каждого :on-change
…
Вариант # 1 — обновление :on-change
в глобальный атом
[:input {:value @new-job-form
:on-change #(dispatch [:new-job-form (-> % .-target .-value)])}]
(reg-event-db
:new-job-form
(fn [db [_ v]]
(assoc db :new-job-form v)))
Вариант # 2 — обновить некоторое локальное состояние, которое отправляется только на глобальный атом :on-blur
(defn text-input
"adapted from:
https://yogthos.net/posts/2016-09-25-ReagentComponents.html
The big idea is this holds local state, and pushes it to the global
state only when necessary"
[{:keys [sub-path disp]}]
(r/with-let [value (r/atom nil)
focused? (r/atom false)]
[:div
[:input
{:type :text
:on-focus #(do (reset! value @(subscribe sub-path))
(reset! focused? true))
:on-blur #(do (dispatch (conj disp @value))
(reset! focused? false))
:value (if @focused? @value @(subscribe sub-path))
:on-change #(reset! value (-> % .-target .-value))}]]))
Второй вариант немного менее медленный, но более медленный, чем просто ввод необработанного текста…
Редактировать:
Вариант # 3 — для полноты, немного другой вариант, адаптированный из TODOMVC от re-frame
(defn text-input
"adapted from re-frame's TODOMVC:
https://github.com/Day8/re-frame/blob/master/examples/todomvc/src/todomvc/views.cljs
note: this is one-way bound to the global atom, it doesn't subscribe to it"
[{:keys [on-save on-stop props]}]
(let [inner (r/atom "")]
(fn [] [:input (merge props
{:type "text"
:value @inner
:on-blur (on-save @inner)
:on-change #(reset! inner (-> % .-target .-value))
:on-key-down #(case (.-which %)
13 (on-save @inner) ; enter
27 (on-stop) ; esc
nil)})])))
[text-input {:on-save #(dispatch [:new-job-form {:path [:a]
:v %}])
:on-stop #(js/console.log "stopp")
:props {:placeholder "url"}}]
Комментарии:
1. Что вы подразумеваете под лагом? Вы действительно испытываете какое-либо видимое падение производительности? Я добился довольно многого, используя любой из двух описанных вами методов, и у меня никогда не было проблем с производительностью.
2. @KubaBirecki да, наблюдается заметное снижение производительности, поскольку текстовое поле с трудом поспевает за моим набором текста. У меня быстрый компьютер, нет другой очевидной причины, по которой он должен быть медленным.
3. Я не совсем понимаю, каковы ваши требования, но я бы начал с рассмотрения re-frame todomvc и того, как это добавляет задачи, и посмотрим, поможет ли это вам начать: github.com/Day8/re-frame/tree/master/examples/todomvc
Ответ №1:
Повторный кадр и reagent
React
на более низком уровне попытайтесь ограничить повторный рендеринг компонентами, которые изменяются. В вашем случае может возникнуть задержка, если другой компонент (или весь пользовательский интерфейс) повторно отображает в дополнение к текстовому полю, которое было единственным, что изменилось.
Пример, основанный на вашем «первом варианте»:
(defn busy-wait [ms]
(let [start (.getTime (js/Date.))]
(while (< (.getTime (js/Date.)) ( start ms)))))
(defn slow-component []
(busy-wait 2000)
(.log js/console "Ouch!")
[:h2 "I was busy"])
(defn main-panel []
(let [new-job-form (re-frame/subscribe [:new-job-form])
(fn []
[:div.container-fluid
(slow-component)
[:input {:value @new-job-form
:on-change #(dispatch [:new-job-form (-> % .-target .-value)])}]
;; etc
Это приводит к slow-component
повторному рендерингу при каждом вводе текста и действительно запаздывает, поскольку slow-component
для рендеринга требуется не менее 2000 мс.
В приведенном выше случае простым решением является предоставление функции slow-component
как функции для повторного кадрирования, превращая вызов в вектор, т. Е.:
[:div.container-fluid
[slow-component]
Это позволяет re-frame видеть, что slow-component
повторный рендеринг не требуется, поскольку его данные не изменились. Мы пропускаем это рассуждение, когда сами вызываем функцию в исходном примере:
[:div.container-fluid
(slow-component)
Хорошей практикой также является использование компонентов Form-2 при привязке к подпискам.