Fork me on GitHub
#re-frame
<
2020-07-02
>
bringe02:07:35

Hello, got a quick question. I've been learning re-frame lately and came across a scenario in my app where I want to call .focus on an html element that only appears in the dom after an event (registered with reg-event-fx) is handled. I made the call to focus the element an event with reg-event-fx and used a dispatch effect in the causal event with the ^:flush-dom metadata on the event vector to make sure the element is rendered before .focus is called. If it weren't for needing that flush though, I would just use reg-fx. Is there no way to flush the dom before an effect registered with reg-fx is run?

bringe02:07:34

Here's the code. If it weren't for needing to make sure the dom was rendered before ::focus-html-element , I would just use reg-fx to register it, because I'm not changing the db or returning any effects. Just wondering if this is idiomatic I guess.

mikethompson02:07:58

To avoid complications, you could perhaps use the auto-focus attribute?

(defn view 
  []
  [:input {:type "text" :id "fname" :auto-focus true])
This only works once, when the widget is first rendered which may not give enough control, but it sure is simpler.

bringe16:07:01

Ah, thanks! I was not aware of this.

Pavel Klavík02:07:16

Also I think it doesn't work in Safari which is sadly IE of the current time 😞

mikethompson02:07:04

If that doesn't give you enough control, you could create an effect which hooks into Reagent's after-render

mikethompson02:07:23

(reg-fx 
  :focus-html-element
  (fn [element-id] 
     (reagent/after-render  #(some-> js/document (.getElementById element-id) .focus)))

👍 3
bringe17:07:01

This gave me the control+simplicity I was looking for. 🎉

mikethompson02:07:58

Note the use of some-> to avoid any weird corner cases

Mikko Koski06:07:13

@brandon.ringe I tend to leave re-frame out of that type of very view specific logic / close to DOM and use Reagent Form-3 components for that. Something like this should do the trick:

(defn focus-input []
  (let [ref (atom nil)]
    (r/create-class
     {:component-did-mount
      (fn [_]
        (.focus @ref))

      :reagent-render
      (fn [_]
        [:input {:ref #(reset! ref %)}])})))

👍 3
bringe16:07:03

Interesting, this looks possibly more efficient than the .getElementById method since it avoids the lookup. Maybe not significant, but still.

bringe16:07:14

Thanks for all the info! Great solutions

shaun-mahood23:07:07

@brandon.ringe Here’s one more that should work - if you want to use it and have trouble let me know and I’ll double check (writing code on my phone is not always a good idea) [:input {:ref #(when % (.focus %))}]

👍 6