Fork me on GitHub
#hoplon
<
2023-05-21
>
Lidor Cohen08:05:25

Hello! I have a (hopefully) small question. When I do this:

(def ctext (cell "Hello!"))

(defn hello []
  (h/div
   ctext
   (h/input {:input #(reset! ctext (-> % .-target .-value))})))
On each char I type in input the input lose focus, why is that? (it seems to rerender for some reason)

Lidor Cohen13:05:51

This solves the issue:

(h/div
   (h/text ctext)
   (h/input {:input #(reset! ctext (-> % .-target .-value))}))
Look like something related to the rerender of div with direct text that causes the rerender of input

Lidor Cohen16:05:38

What is the reason h/text has to be a macro?

alandipert22:05:12

it doesn't have to be, but h/text supports interpolation

alandipert16:05:15

looked into this a little more, i think the relevant code is here: https://github.com/hoplon/hoplon/blob/master/src/hoplon/core.cljs#L299-L300 in your first example, ctext is a cell and goes down the first, (`cell? child)` path. in that case, when the value of child changes, the kids cell<vector> associates element at i (size of kids vector at append time) with the ctext cell's new value in the second example, (h/text ctext) evaluates to a DOM textNode and not a cell: https://github.com/hoplon/hoplon/blob/master/src/hoplon/core.clj#L229-L230 in the second example, the DOM textNode is "static" in the sense that its contents can change internally over time, but as far as the kids cell<vector> it's contained in, it never changes. its value is mutated independently by the cell created in h/text so, back in -append-child!, the non-cell gets appended via (swap! kids assoc i child) zooming out, this touches on some of the design implications of a relationship between data model and DOM in a "retained mode" graphics model. and also the stange algebra of cell/non-cell composition that emerges

alandipert16:05:28

finally, i think the first example loses focus because changing ctext modifies the div's kids, and the children get removed and re-attached whenever the ctext cell changes

alandipert16:05:01

anyway, hope that all makes some kind of sense, happy to elaborate in any direction in case not

Lidor Cohen12:05:45

Another question: what is the difference?:

(cell= (str "Hello, " (cell "John")))

(let [a (cell "John")]
    (cell= (str "Hello, " a)))

((formula str) "Hello, " (cell "John"))
the 1st example returns "Hello, [object Object]" the 2nd & 3rd return "Hello, John"

alandipert23:05:43

in the first example a cell is created inside a formula and the enclosing cell= is not generating code to derefence the cell before passing to str

alandipert23:05:12

i think it's because cell= wraps free variable names with maybe-dereference magic, but arbitrary expressions (`(cell "Jon")` are not

alandipert23:05:10

note also that example 2 is representative by far the most common usage, naming a cell outside of a cell= so you can manipulate it

Lidor Cohen09:05:48

if I have the following use case:

(def m {:a (cell 1)
        :b (cell 2)})

(cell= (+ (m :a) (m :b)))
where I want to bundle some cells in a structure, and compute over them. please note I'm already aware of the option of let:
(let [a (m :a)
      b (m :b)]
  (cell= (+ a b)))
The catch here is that I also get the computation as a variable and I can't break the computation and extract the cells out, think of something like this:
(def m {:a (cell 1)
        :b (cell 2)})

(def c (+ (m :a) (m :b)))

(cell= c)
Is there any solution that you can recommend?

mynomoto11:05:30

You could have a bigger cell if that is not a problem in you use case:

(def m (cell {:a 1
              :b 2})

(cell= (+ (m :a) (m :b)))

Lidor Cohen11:05:40

But I lose granularity, correct?

mynomoto11:05:12

You mean you will have fewer cells that will trigger bigger updates on formula cells? This is correct but in my experience not a practical problem. I'm not sure about the problem that you are trying to solve to offer other options of how to do it.

Lidor Cohen11:05:50

The problem I'm trying to solve is quite complex and boil down to having cells in a structure and being able to create reactive computations based on them without explicitly declare them (the cells).

mynomoto12:05:30

I think formula could work in that situation, the 3rd option in your initial message in this thread.

Lidor Cohen08:05:01

but how would you break c:

(def m {:a (cell 1)
        :b (cell 2)})

(def c (+ (m :a) (m :b)))

(cell= c)
into its operation and operands? the are "applied away". And if its a more complex expression it will be even harder, this is basically what cell= does.

mynomoto12:05:27

(def m {:a (cell 1)
        :b (cell 2)})

(def c (formula +))

(def result (c (:a m) (:b m)))

(cell= (js/console.log {:result result}))

mynomoto12:05:59

I think this is nearer what you want. Not sure if near enough.

alandipert17:05:08

note also that when dealing with structured updates, lenses can be useful