Fork me on GitHub
#fulcro
<
2018-10-21
>
hmaurer15:10:01

Is it possible to work with react refs with fulcro?

wilkerlucio15:10:50

we usually use the fn version of it, I personally usually write like this:

wilkerlucio15:10:09

(dom/div {:ref #(gobj/set this "refName" %)})

wilkerlucio15:10:21

[goog.object :as gobj] to require gobj

wilkerlucio15:10:55

then you cna get it with (gobj/get this "refName") in the places you need it

thheller15:10:07

be careful with re-creating the :ref fn in each render. that will call the ref fn twice per render. one with nil and once with the node

wilkerlucio15:10:38

really? isn't this the React recommended way of doing it?

thheller15:10:19

using a function is recommended yes. just create it in the constructor or so

hmaurer15:10:12

@U066U8JQJ ahthanks! why do you use goog.object there? this is not a clojure object/map?

wilkerlucio15:10:50

this is the react componet, so a JS object

tony.kay17:10:36

And the :initLocalState is your constructor, so you can even put them in component-local state with:

:initLocalState (fn [] 
  {:save-ref (fn [r] (gobj/set...))})

hmaurer15:10:57

also, quick question: how non-idiomatic would it be to have data in the global state that doesn’t directly control the UI? By this I mean: for some feature I need to store whether an input is focussed or not (to display a toolbar when focussed), but the focus state of the input wouldn’t be controlled by the data.

hmaurer15:10:07

I can’t see a way around it though

wilkerlucio15:10:33

@hmaurer why you cant store in the regular app state?

hmaurer15:10:53

that’s what I meant sorry; in the app state

wilkerlucio15:10:32

can you clarify what you mean by global state that doesn’t directly control the UI?

hmaurer16:10:35

that was poorly phrased. What i mean is that usually you would want your UI to be a pure function of state, so that you can time-travel, etc. In this case I would have a piece of data in the state indicating whether the input is focussed or not, but the focus state of the input would not depend on the state

hmaurer16:10:16

one-way data binding if you will

wilkerlucio16:10:23

you can still use the app state for it, take load markers for example, they are not part of the component UI directly, you can create a custom marker for you in the app state root, and make any component look at it

wilkerlucio16:10:59

for example, lets say you have an input with this:

(dom/input {:onFocus #(fp/transact! this [`(set-focus-marker {:focus true})])
            :onBlur #(fp/transact! this [`(set-focus-marker {:focus false})])})

hmaurer16:10:23

yep basically that’s what I would have

wilkerlucio16:10:39

then you get the mutation:

wilkerlucio16:10:40

(fm/defmutation set-focus-marker [{:keys [focus]}]
  (action [{:keys [state]}]
    (swap! state assoc :input-in-focus focus)))

wilkerlucio16:10:48

now, if you need to know about this in a component, do a root query for it:

wilkerlucio16:10:03

[:something/id :bla :other-data [:input-in-focus '_]]

wilkerlucio16:10:30

then you will have :input-in-focus available in the props

tony.kay17:10:36

I don’t recommend trying to “store” focus

tony.kay17:10:47

it is a stateful browser thing…if you mean an input

tony.kay17:10:50

you’ll get out of sync

tony.kay17:10:40

use component local state, refs, and lifecycle for that…just normal react stuff…I guess you could “track” that in app state

tony.kay17:10:26

In some of my apps I’ve need to deal with refs on a heavy level (e.g. drag and drop)…I’ve done things like created global atoms to store current refs by component ID

tony.kay17:10:47

not app state, though, because the DOM elements are not serializable for app state…and will break features

tony.kay17:10:53

but either way…DOM “state” like focus doesn’t belong in app state IMO, because it isn’t enforceable by react, so it has no real meaning for it to be in the state that is supposed to be a pure representation of UI reality.

hmaurer17:10:49

That was my feeling as well, which is why I asked here. thanks!

hmaurer17:10:57

@tony.kay thanks for the lengthy reply! My reson for storing focus is two-fold: (1) I want to display a toolbar under the input when it is focussed (2) I need to be able to insert “special characters” in my input when pressing buttons in the toolbar (greek letters etc). The way I did this in the past using a vanilla react component was to store the current selection range in the react component’s state. Then, when inserting a special character, I would update the input’s value (which was controlled by the state) and AFTER the state update completed (in a setState callback) I would set the selection range to the appropriate value on the input (i.e. cursor position + 1 for the new character) and focus it

hmaurer17:10:12

it seems a bit messy said like this but it’s the only approach I could find

tony.kay17:10:46

Sometimes the low-level DOM stuff is messy…React cannot control certain things, and doing low-level c-local crap is all you can do…

tony.kay17:10:14

Fulcro relies on React…so Fulcro really can’t do the DOM stuff any “better”…it isn’t doing any DOM stuff itself 🙂

tony.kay17:10:41

There are literally 2-3 react calls in the entire source code of Fulcro…render and createElement

hmaurer17:10:52

fair 🙂 Speaking of, is it easy to use existing react component librairies with Fulcro? i.e. BlueprintJS

tony.kay17:10:54

and renderToString for SSR….maybe one more?

tony.kay17:10:07

yep…you just have to translate CLJ data structures to js

hmaurer17:10:13

ah so defsc etc are really jsut dumb wrappers on top of react?

tony.kay17:10:27

not dumb…but not special in the DOM area

tony.kay17:10:41

Fulcro is about state, full stack interaction, etc.

tony.kay17:10:57

but yes, defsc just makes a React component

currentoor17:10:45

I’m trying to get the new pessimistic mutations working

(defmutation login [params]
  (action [{:keys [state]}]
    (auth/remove-token)
    (swap! state dissoc ::user/current-user)
    (log/info "Attempting to login"))
  (ok-action [{:keys [ref state] :as env}]
    (log/info "Authentication Successful")
    (util/spy :pp (get-in @state (conj ref ::pm/mutation-response))))
  (error-action [env]
    (log/error "Authentication Unsuccessful"))
  (remote [{:keys [ast state] :as env}]
    (pm/pessimistic-mutation env)))

currentoor17:10:12

but the ok-action is being triggered even though the server is throwing an exception

currentoor17:10:29

(get-in (clojure.core/deref state) (conj ref :fulcro.incubator.pessimistic-mutations/mutation-response)) => {:fulcro.client.primitives/ref
 [:ucv.ui.root.signon/form-control :singleton],
 :fulcro.incubator.pessimistic-mutations/status :hard-error,
 :fulcro.incubator.pessimistic-mutations/error-marker :Sad-face,
 :fulcro.incubator.pessimistic-mutations/low-level-error
 {:type "class clojure.lang.ExceptionInfo",
  :message "Incorrect email or password", 
  :data {}}}

tony.kay17:10:40

probably a bug

tony.kay17:10:56

though I thought I tested that in the ws card

tony.kay17:10:02

you on 0.0.3

tony.kay17:10:06

not snapshot, right?

currentoor17:10:10

[fulcrologic/fulcro-incubator "0.0.3-SNAPSHOT"]

currentoor17:10:15

[fulcrologic/fulcro “2.6.9”]

tony.kay17:10:52

headed to bfast, be back later

tony.kay17:10:58

SNAPSHOT wasn’t updated with final fixes

tony.kay17:10:03

I did a release

currentoor19:10:32

0.0.3 didn’t work either

tony.kay20:10:34

hm….sounds like a bug then 😕

tony.kay20:10:23

@currentoor you’re using the new pessimistic-mutations namespace, right? Not the ones in db-helpers

tony.kay20:10:27

oh, verified…network errors do the wrong thing

tony.kay20:10:32

I just saw it in my cards 😞

tony.kay20:10:35

oversight on my part

tony.kay21:10:48

I’ll fix it later…just assume it will work in 0.0.4 (network errors are rare anyway..so you can get started)