Clojurians
# om

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

peeja 00:53:41

@krchia: I think you're looking for update-state!

krchia 01:01:11

@peeja: not sure what’s the difference between update-state! and set-state! - the docs aren’t making it clear for me

peeja 01:01:30

update-state! takes a function, like update

peeja 01:01:59

set-state! just takes the new value for the state

peeja 01:06:49

Ah. What's the thing you're mutating?

krchia 01:07:05

are you familiar with d3?

krchia 01:07:17

perhaps you might know a better way - i’m just working with the d3.layout.tree structure

krchia 01:08:12

just trying to implement this example, where you push new nodes into both the nodes structure and as a children of a parent node

peeja 01:08:20

Typically in d3 you're just getting new data an re-drawing your d3 stuff over the existing DOM, correct?

krchia 01:08:28

@krchia uploaded a file: Untitled

peeja 01:08:31

Ah, link?

peeja 01:09:32

I would move the generation of the data out of your component entirely

peeja 01:09:54

Your component can take in data, then on did-update it can redraw the d3

krchia 01:09:58

sorry, what does that mean ?

krchia 01:10:12

so i keep it in app-state or a global variable ?

peeja 01:10:14

You're generating random data inside the component

peeja 01:10:24

Keep it in whoever's calling this component

peeja 01:10:49

Just don't complect generating the data with rendering the data

krchia 01:11:38

i’m not doing that for now - to make things simple i have a vector of pre-generated data to test it with

peeja 01:11:48

Ah, then that makes this easy:

peeja 01:11:53

Just pass that data into your component

peeja 01:12:33

Each time your component gets different data, it'll re-render. The actual render isn't going to be interesting, but it'll also call will-update, and that's where you call your d3 code from.

peeja 01:12:45

(with the new params your component has just received)

krchia 01:13:23

did you mean did-update?

peeja 01:13:29

Yeah, sorry, that's right

peeja 01:13:45

(it could be either, really, but did-update is more appropriate)

krchia 01:14:25

ok, hang on a sec while i try this stuff out

krchia 01:14:32

i already have my d3 code in did-update

krchia 01:14:48

i’m just going to take out the data from the local component state

peeja 01:15:05

The trick is that it sounds like you're passing in non-CLJS objects as the data. Is that right?

krchia 01:15:50

@krchia uploaded a file: Untitled

krchia 01:16:23

yeah, should i just apply clj->js only when i’m going to render them ?

krchia 01:16:46

and prepare it with clojurescript operations

peeja 01:16:58

Hmm, there's all kinds of surprising stuff in here…

peeja 01:17:06

Why the go?

krchia 01:17:19

yeah, it’s not an exact copy, it’s suppose to be rendering game trees of 2048

peeja 01:17:29

Ooh, neat!

krchia 01:17:53

perhaps it’s not the right way? using it to imitate setInterval

peeja 01:18:55

Oh, I see. Yeah, fair enough, I think that makes sense

krchia 01:19:24

it’s super messy, sorry bout that

peeja 01:19:44

The reason I'm calling out passing in JS objects as the component's data is that Om depends on being able to = the old params and the new params to see if they've changed

krchia 01:20:24

so if i want to (.push some-component-data foo)

krchia 01:20:34

what happens?

peeja 01:20:39

Nothing

peeja 01:20:47

It won't notice

krchia 01:21:00

what’s the idiomatic way of doing this?

peeja 01:21:11

You want as much of this to be immutable as you can get away with

krchia 01:21:12

push, then set-state or convert back to clj and set state?

peeja 01:21:20

I'm not sure what the pushing is for

peeja 01:21:42

Oh, I see what you're doing

peeja 01:21:53

No, you just want to reimplement this with immutable data

krchia 01:21:59

ah, it’s the “add a new node to a random parent” part in the d3 code, because if i don

peeja 01:22:14

You don't want to .push onto an existing object, you want to conj onto a vector

krchia 01:22:16

don’t push in the children into the parent’s node, then it doesn’t render

peeja 01:22:34

Then you want to pass the new vector into the component

krchia 01:23:04

so convert it back to clj, then render it using the js data

peeja 01:23:07

So, if you want this to run as an animation, you'll want to have something on the outside that re-renders the component every X ms with the new data

peeja 01:23:26

convert it back to clj?

krchia 01:23:42

sorry, i thought d3.transition will take care of the work

krchia 01:23:56

i just need to update the component state

krchia 01:24:12

yeah, i store the tree data structure (to be rendered in d3) as an js object

peeja 01:24:28

Ah, you know what, to really use Om, you'll want to put the node data structure in the app state, and then kick off something that changes that state over time

peeja 01:25:28

So, each tick, the app state atom points to a new tree of nodes, exactly like the last, but with one random node added

peeja 01:25:47

Then Om notices that the state atom has changed, so it passes that value into the component

peeja 01:26:07

Then the component converts that into more JS-y values (if necessary), and passes that to d3

krchia 01:26:15

it works with local component states as well right?

krchia 01:26:25

sorry for the slow replies - my hands are kind of bandaged up

peeja 01:26:35

Aw, I'm sorry :disappointed:

peeja 01:26:41

Yeah, it'll work with component state too

peeja 01:27:08

It just means putting the ticking mechanism inside the component (which I guess is what you've done)

peeja 01:27:14

That should work fine

krchia 01:28:14

yeah, thanks :slightly_smiling_face: you’ve been great

krchia 01:28:40

i think i’m going to try that approach - storing the data in clojure data structures, then converting to js data only when d3 needs it

anmonteiro 01:29:22

@krchia: I haven’t been following the whole discussion, but for performance you might want to keep JS datastructures

peeja 01:29:29

:thumbsup: Good luck!

krchia 01:30:02

@anmonteiro: what - i was just about to switch back to my editor!

anmonteiro 01:30:04

and deal with the Om diffing part by having a monotonically increasing value in your local state that you increment whenever you mutate the JS object

anmonteiro 01:30:31

js->clj and clj->js is heavy on performance

krchia 01:30:46

that sounds like an interesting approach, did you consider this approach @peeja

peeja 02:31:40

Oh, yeah, that works too.

peeja 02:32:23

I tend to stay away from mutation as much as possible until it actually starts hurting :slightly_smiling_face:

peeja 02:32:31

but that's a reasonable way to handle it

krchia 05:52:51

i’m a little confused about how IDidUpdate works

krchia 05:53:17

my most basic understanding is that it’s called whenever the component state or app state changes

krchia 05:54:27

but i’m not sure why it’s called twice when i make a single change with om/set-state

peeja 16:32:36

@krchia: Your understanding sounds correct to me. It should be called just after render and after the DOM is updated. Are you maybe calling set-state! from inside did-update, triggering it a second time?

viebel 16:59:10

Can somebody help me to understand why does Om Next component not re-render when state changes?

anmonteiro 17:04:37

@viebel: that SO example will never re-render because the queries are not composed correctly

anmonteiro 17:04:49

maybe you’re having a similar problem with your query structure?

viebel 17:07:19

Let me check

viebel 17:10:36

@anmonteiro: Two questions: 1. What is not correct in the query composition
2. If it’s not correct, why does it work when updating the state from the REPL

anmonteiro 17:11:54

@viebel: so the components have queries like: ``` (defui Child static om/IQuery (query [this] [:foo]))

(defui Parent static om/IQuery (query [this] [:parent])) ```

anmonteiro 17:12:10

when you add-root!, you call that with the Parent component

anmonteiro 17:12:39

but there’s no composition in the queries, so Om Next can’t know that there’s a child component somewhere

anmonteiro 17:13:08

the correct composition would be something like this for the parent’s query:
[:parent {:child (om/get-query Child)}]

anmonteiro 17:14:24

state updates because you’re using normal constructs like swap!. But for Om Next there exists no components below the Root, so it doesn’t know that it has to update

viebel 17:21:54

In my case, I have query composition. Render is called on user event but is not called when I am updating the state without any user event (I am triggering a state update every 3 seconds)

viebel 17:22:00

Is it clear?

anmonteiro 17:23:00

@viebel: do you have a link to the example or can put a minimal case together?

anmonteiro 17:23:18

multiple things might be going on so I can’t give a clear answer

viebel 17:24:32

I want to trigger code evaluation if the user press Ctrl-Enter and after 3 seconds of inactivity

viebel 17:24:49

The 1st one works fine. The second one is broken (it used to work)

anmonteiro 17:28:19

@viebel: right, where is the actual code that updates the state?

anmonteiro 17:32:36

@viebel: A number of things might be happening, the example it just too conflated

anmonteiro 17:32:46

esp. with the core.async bits

anmonteiro 17:33:02

I suggest you make a minimal case I could look at

anmonteiro 17:33:11

I don’t have time to grok everything this code is doing

viebel 17:33:20

I understand.

viebel 17:33:27

Thanks for your time

akiel 21:12:09

Can I return two queries from a read function to a remote? I tried an AST with type :root and two children, but then my send function get a query like [[q1 q2]] which is one vector to much.

anmonteiro 21:46:24

@akiel: I think you'll need to override merge-sends in the reconciler

akiel 21:47:21

But normally one is supposed to return only an AST fragment not a root AST? (as the docu says)

anmonteiro 21:49:52

I can't recall what the documentation says, but the existent hooks in the reconciler allow you to extend the defaults

anmonteiro 21:50:18

So I'd say either is correct

akiel 21:51:21

The docu says: Remote query entries must be query expression AST fragments that correspond to the :remotes specified to the reconciler.

akiel 22:19:26

@anmonteiro: As I see it, merge-sends doesn’t solve my problem. But thanks nevertheless.