Input field blinks before update in re-frame

I am building a re-frame application and use re-com DOM elements, among others the re-com/input-text element. The value in the text element is not pushed to the app-db until the input loses focus, which is the default behavior.

One really annoying thing when I have 10x and spec checking of the app-db turned on, meaning that it takes some time after the new value is dispatched until it is saved, is that the text in the input field reverts to its old value until it gets the new value back again from the subscription. That is, after leaving the input field, the old value is shown for a short time, and then the new value is shown again.

Basically, the code looks like this

(re-frame/reg-sub :test-sub
  (fn [db]
    (:test-sub db)))

(re-frame/reg-event-db :test-event
  (fn [db [_ value]]
    (assoc db :test-sub value)))

(defn main-panel []
  (let [test (re-frame/subscribe [:test-sub])]
    [re-com/v-box
     :height "100%"
     :children [[:span @test]
                [re-com/input-text
                 :model test
                 :on-change #(re-frame/dispatch [:test-event %])]]]))

Can I prevent this behavior somehow?

This is also noticeable, although the delay is much shorter, in the lightweight re-frame template app. I’ve made a quick demonstration of the phenomenon available at https://github.com/brjann/re-frame-test

On your computer is it visible at all on the re-com docs? I can’t see the effect.

If you weren’t using re-com I’d suggest using an uncontrolled textarea, where you subscribe :default-value of the textarea to test-sub instead of subscribing :value to test-sub. Then you can control exactly when the textarea will re-render by updating the key metadata. Rough example using Reagent, but same thing applies in Re-frame:

;; A way to force an uncontrolled textfield to re-render is to change the key
(defn uncontrolled-with-key
[text]
[:form {:no-validate true
        :auto-complete "off"}
^{:key @!key}
[textfield {:id "uncontrolled-inner"
            :multiline true
            :style {:width 300
                    :font-size 1}
            :variant "outlined"
            :label "Uncontrolled"
            :default-value @!text}]])

(comment
; Resetting the text atom won't make the textfield re-render
(reset! !text "Chocolates")
; But changing the key will
(swap! !key inc)
:pass)

Thanks @tobyloxy

The re-com demo page doesn’t seem to use re-frame, so the effect does not appear there.

I would prefer to use regular input fields for this input if possible. Maybe :default-value can be used on that element as well. I’ll try!

I’ve submitted an issue in re-com’s github repo https://github.com/day8/re-com/issues/219

You might want to use re-frame/dispatch-sync instead of the (deferred) re-frame/dispatch.

And call reagent.core/flush after that, otherwise the view re-render maybe still be delayed

I was not aware of reagent.core/flush. Good to know, thanks!

Thanks @greinseth and @LucyWang000. dispatch-sync did the job. But it feels kind of hacky, I suspect that there is something not right with the re-com input component? If I see further delays, I’ll try to add reagent-core/flush as well.

Thanks again!

Hi. To anyone researching this issue, let me add my 2c (2 comments):

  1. dispatch-sync is indeed hacky, don’t do it.
  2. I think the blink is actually a re-com bug. I’ve analyzed the issue in detail here: https://github.com/day8/re-com/issues/219#issuecomment-729728022
1 Like