Fork me on GitHub
#om
<
2015-10-26
>
paulb01:10:27

can Devcards be used to test Om Next components that mutate state?

paulb01:10:47

but couldn't really understand it, like for example how to get the counter component from the Basic Tutorial to work as a devcard

mhuebert01:10:51

@paulb have you gotten devcards working with other components? I was able to pass in components created via defui & om/factory with no trouble

mhuebert01:10:26

@paulb but I was not using om next to manage state yet, so I’m not sure how the updates would work

paulb01:10:24

@mhuebert yes, I can can set prop values and see what the component looks like, but don't know of a way to test how the component functions when interacted with

mhuebert01:10:05

@paulb ok. I’ve just started figuring out om mutations myself and am not yet clear on how it all works.

mhuebert02:10:47

Tiny non-working recursive tree in om/next: https://gist.github.com/mhuebert/dc1acc48b7c192cc49fb I would have expected queries in child components to run, but they are ignored. it seems all data must be passed down from the top? (when I try that, data flows down appropriately but I run into a "No queries exist for component path” error when I try to mutate)

dvcrn04:10:46

wohoo this works 😄

dvcrn06:10:36

when using om next, does a flux architecture make sense? or is there something that would fit better?

rarous07:10:08

@dvcrn: Om.next is the right architecture

dvcrn07:10:48

looks like I spoke too early with react native. Seems like it was more a lucky hit because of some pre-loaded code

dvcrn08:10:46

mh yeah the add-root! part is what iOS doesn't like

dvcrn08:10:33

In particular the (gdom/isElement target)

dvcrn08:10:45

hrrrmm Error: Invariant Violation: ReactDOM.render(): Invalid component element. This may be caused by unintentionally loading two independent copies of React.

dvcrn09:10:01

all right, time to call it a loss. Overwriting js/ReactDOM with js/React seems to solve a lot of the problems as the API is still very similar. I also changed add-root! to not use gdom, but the final error I couldn't manage to get around (yet) is You cannot render into anything but a top root. Tried saving the reference of the top root component from index.ios.js and using that, but still didn't work. I guess I'll just wait a little until om next is at a more stable point or react native adapts react 0.14 fully. If anyone has tips of things I could try, please ping me!

thosmos09:10:21

Is anyone here using react-bootstrap with Om or Om Next?

chipf0rk09:10:35

I started playing with Om Next and I'm at the end of the "Components, Identity & Normalization" wiki article - for some reason, I only end up with a console error "Cannot read property 'render' of undefined". I tried copying the full code from the Appendix, but it's all the same - where should I look?

mhuebert10:10:45

ok, another go at a recursive tree in om/next - I tried to make the queries like the examples: https://gist.github.com/mhuebert/dc1acc48b7c192cc49fb it renders appropriately, but after my first mutation I get a "No queries exist for component path (layout.core/Root layout.core/Leaf)” error (stack trace is in a comment on the gist)

mhuebert10:10:34

@chipf0rk is there a reference somewhere to js/React.render? that should now be js/ReactDOM.render, changed in recent version of React

chipf0rk10:10:45

nope, it's thrown from the kick-off part, at om/add-root!

chipf0rk10:10:52

No explicit fn calls to React at all

mhuebert10:10:07

what version of om?

chipf0rk10:10:45

1.0.0-alpha9, but I also tried with 1.0.0-alpha8, as referenced at the beginning of the article

mhuebert10:10:26

@chipf0rk I had to add :exclusions [cljsjs/react cljsjs/react-dom] to some other dependencies in my project (sablono, devcards) in order to get the correct (om-specified) version of react to load.

mhuebert10:10:56

they were loading earlier versions in which ReactDOM was set to null

chipf0rk10:10:12

Hmm, my deps are only clj, cljs, om and figwheel-sidecar (also like in the article)

chipf0rk10:10:34

I'm reasonably certain I've followed all the instructions, is what I'm saying :(

mhuebert10:10:47

weird. can you click on the error in the console to view source where it occurs?

mhuebert10:10:22

also in the console you can type ReactDOM.render to verify that it exists

chipf0rk10:10:46

the funny thing is, ReactDOM is undefined, but it shows up in the console's autocompletion when I start typing "Rea..."?!

mhuebert10:10:00

if you type React, what version shows up?

mhuebert10:10:35

ok. when this happened to me it was from some other dependency loading an old version of react

mhuebert10:10:47

if you look at the source of om it has v 14 in dependencies

mhuebert10:10:56

somewhere 13.3 is being loaded and clobbering ReactDOM

mhuebert11:10:10

i’ve got to run, bbl

chipf0rk11:10:27

good directions, thank you!

thosmos11:10:24

@chipf0rk: you can do "lein deps :tree" to see what's requiring what

dvcrn11:10:43

@chipf0rk: for me it was sablano which pulled a older react version in

dvcrn11:10:51

in case you're using that

dnolen11:10:25

@dvcrn: it’s just a bug we have references to ReactDOM in the reconciler, should probably parameterize that

dnolen12:10:01

@dvcrn: you don’t need flux if you’re using Om Next, all the flux stuff becomes plumbing

chipf0rk12:10:15

Well, it's working now. lein deps :tree only showed what om pulled in and it was all 0.14.0; however, react.inc.js was at 0.13.3 for whatever reason and wouldn't change even after pulling deps and restarting figwheel. I killed the whole js/ directory and my ~/.m2/ too, then restarted – that fixed it

dvcrn12:10:48

@dnolen: I'm planning to invest a few more hours tomorrow into making it run with native at least once. Do you have thoughts on a workaround for that bug I could try?

dnolen12:10:06

clone the repo, edit it.

dnolen12:10:39

parameterize js/ReactDOM.render so that instead that becomes some user supply-able thing in the reconciler :config

dnolen12:10:51

:render-root or something.

dvcrn12:10:00

great, i'll try that simple_smile

dnolen12:10:29

@dvcrn: let me know if that works for you and I will fix it in master if it does

dvcrn12:10:45

:thumbsup:

drcode14:10:24

Hi, getting error "No queries exist for component path (lisperati.core/Parent lisperati.core/RectLayout)" https://gist.github.com/drcode/617351ed39533e11ff43 on alpha9

drcode14:10:23

I have a parent component that is using a rather generic "Rectangle Layout" component to manage it's children

drcode14:10:57

I suspect that I'm doing something in the Parent class either in render() or in query(), because both functions manage both the RectangleLayout class and the Child class objects

dnolen14:10:28

so people seem to misunderstand how query works even given all the examples I’ve made simple_smile

drcode14:10:53

Yes- where am I wrong? simple_smile

dnolen14:10:54

every components’ query should be a complete query on it’s own

dnolen14:10:06

your parent is taking the child’s query and mucking with it

dnolen14:10:20

it does not actually have it’s own query

dnolen14:10:25

this isn’t going to work

drcode14:10:12

ok, let me think over that- Thanks for the info!

dnolen14:10:55

if you have two components and they implement IQuery

dnolen14:10:21

your total query expression better look like [… {:foo/bar […]} …]

drcode14:10:47

> (om/get-query Parent) [{:rects [:db/id :rect/dimwidth :rect/dimheight :rect/color :rect/width :rect/height :rect/left :rect/top]}]

dnolen14:10:12

that doesn’t match what I said

dnolen14:10:54

your code is missing another layer because the parent is abusing the child query and not providing it’s own

drcode14:10:20

ok I see, thanks!

dnolen14:10:02

every component must provide it’s own query. You cannot reuse the child’s query. This is a modularity violation to do so.

drcode14:10:41

Ok that makes sense

dnolen14:10:53

“every component must provide it’s own query”, if it implements IQuery of course

dnolen14:10:08

none of this applies to pure stateless components

dnolen14:10:08

@drcode your example is something I think we can detect without creating problems so will probably throw an error if you do what you did.

drcode14:10:46

probably makes sense, unless I have a totally unique way of getting it all wrong simple_smile

dnolen14:10:04

@drcode figuring out how to validate queries and transactions is definitely something for me as well others to think about.

dnolen14:10:23

most of the issues people have are not understanding how to separate out their queries and where to run transactions.

jannis14:10:52

I'm playing with writing a basic Kanban app using om.next, with boards that refer to lanes and lanes that refer to cards. And it works! om.next normalization works perfectly, I've got something similar to add-friend (`move-card`) that moves a card from one lane to another, and I even managed to get card drag-and-drop to work with transactions. Amazing! Starting to get the hang of this. Thanks @dnolen for your patience, help and hard work. simple_smile

jannis14:10:10

Perhaps I should turn it into a blog post, just to give people another example app to look at? It currently operates against local state only, so it would be easy to put it on GitHub and allow people to play with it as well.

monjohn14:10:33

@jannis: a write-up would be appreciated

paulb14:10:48

@jannis I would be very interested in that!

dnolen14:10:52

more examples are good, writeups even better as long as there are caveats “alpha software” yadda simple_smile

monjohn14:10:27

@dnolen: I am struggling with the same error of “No queries exist” when calling transact! in a child component. I have gone back over the docs, and can’t find guidance on where they should go. Am I overlooking it?

dnolen14:10:52

@monjohn: here are the current rules of thumb

dnolen14:10:08

1) never run transactions from components who do not implement IQuery

dnolen14:10:30

2) put transactions where they actually belong (children who don’t actually have the context to run a transaction should never invoke it)

dnolen14:10:41

classic example, children shouldn’t delete themselves.

monjohn14:10:24

@dnolen: I didn’t think I was violating those rules of thumb. But I was thinking about what you have written before, so I passed the function in as a callback and it worked. Now I just need to get my head around why. simple_smile

dnolen14:10:08

@monjohn: do you have a gist of what your child looked like?

monjohn14:10:39

I’ll clean up my code and make one.

gardnervickers15:10:38

Hey all, I’m following along with the om-next-demo app on alpha9, why does the send function now recieve a map of the form {:remote [:foo]} instead of just the map.

gardnervickers15:10:06

is that supposed to be the case?

jannis15:10:52

@gardnervickers: Because you can specify multiple remotes now, I believe.

jannis15:10:32

E.g. instead of {:value ... :remote true} in read, you can do {:value ... :remote1 ... :remote 2 ...}.

dnolen15:10:43

@monjohn there’s an error now if you attempt that

dnolen15:10:14

no more calling transact! from pure components

xgavin15:10:27

thanks for all the weekend tweet reading @dnolen

dnolen15:10:45

@xgavin: haha, your welcome

gardnervickers15:10:08

@jannis: So really my :send function needs to be able to handle where to route stuff now?

dnolen15:10:35

if you don’t care about mutiple remotes you will always get a map with one thing in it :remote

dnolen15:10:27

that’s the default, but for users that want to design their application around HTTP caching, the multiple remotes bit makes life a whole lot simpler

shaun-mahood15:10:09

@dnolen: Looking forward to your unsession at the conj. Anything we need to do to say I'm attending or anything like that?

scriptor15:10:59

how is an om.next reconciler able to extract the queries of all the children given a root?

scriptor15:10:25

put another way, how does a reconciler know what a root's children are?

dnolen15:10:18

@scriptor: we’re going in implementation detail territory, but ...

dnolen15:10:30

reconciler doesn’t know anything it uses the indexer

dnolen15:10:53

indexer maintain several kinds of useful indexes

dnolen15:10:46

currently don’t maintain child parent relationships, but this doesn’t seem like useful information to me - or not useful enough to track

dnolen15:10:21

the indexer does index query templates for all known component classes

dnolen15:10:53

so given any “class path” the indexer knows what query template is associated with that path.

scriptor15:10:57

okay, so the reconciler uses the indexer to figure out all the different queries and merge them together

dnolen15:10:04

so we map Root -> Foo -> Bar to some query

dnolen15:10:19

no reconciler doesn’t merge queries

dnolen15:10:36

reconciler really doesn’t do anything

dnolen15:10:47

there’s a queue of keys

dnolen15:10:09

it asks the indexer for components that match those keys, it re-renders those components

dnolen15:10:34

it re-renders them with props determined by re-running queries for these components (which the indexer delivers)

dnolen15:10:18

the indexer knows everything - the reconciler just keeps things in sync and makes remote requests on your behalf.

dnolen15:10:47

the indexer isn’t a part of any public api though

dnolen15:10:56

for the obvious reasons

scriptor15:10:04

does the reconciler know which keys are already cached and which aren't?

dnolen15:10:20

that’s a user problem

scriptor15:10:27

right, that's the parser

dnolen15:10:27

you sort that out when you write your parser

dnolen15:10:14

so people keep asking about Flux ...

dnolen15:10:35

the indexer + parser allows us to just convert Flux into plumbing

dnolen15:10:51

parser is your actions, and indexer gets users out of writing manual updating code

scriptor15:10:30

that makes a lot more sense now, thank you

bbss15:10:14

@jannis such a blog post would be appreciated, I am doing something similar with lanes. But not at your stage yet simple_smile

bbss15:10:41

However getting closer and closer, I am also trying to use datascript

scriptor15:10:47

just looked into the remote sync tutorial, it looks like DashboardItem explicitly calls om/get-query, on the child elements (Post, Photo, Graphic)

jannis15:10:00

@bbss: Cool! I'm working on the demo code right now simple_smile

jannis15:10:17

Would be interesting to share our results.

dnolen15:10:42

@scriptor: that’s not a problem

dnolen15:10:11

you compose queries by explicitly asking the component class you intend to use.

dnolen15:10:30

you don’t know what they will be but you do know what child components are involved

dnolen15:10:40

it’s necessarily true since you are writing the parent

scriptor15:10:46

@dnolen: yep, I was just foggy on how a parent's query gets those child queries, seeing that code cleared things up

dnolen15:10:19

@scriptor: right you could jump around randomly but I do suggest reading the tutorials in order from beginning to end simple_smile

scriptor15:10:46

true, I read the quick start, was just getting a bit curious how nested components might work

scriptor15:10:16

just noticed the components, identity, normalization shows a similar example, d'oh

mhuebert16:10:02

is it possible for the results of a parent query to influence a child component’s query? ie. a parent component fetches a list of IDs, then child components fetch data associated with those IDs

mhuebert16:10:20

it looks like query params can’t read props

mhuebert16:10:13

trying to render a recursive tree, wondering how, say, a mutation that only affects a leaf node would trigger only that leaf to re-render, if the entire tree is passed down from the root in a single query

dnolen16:10:57

@mhuebert: you mixing up what queries do (nothing)

dnolen16:10:01

and what parsing does (everything)

mhuebert16:10:30

so it is not correct to say that a parent or child ‘fetches’ things with queries

mhuebert16:10:58

rather they specify queries, which the parser turns into data

mhuebert16:10:06

does all data always flow from the root component? ie. its query reaches down into the tree w/ get-query (and a child component does the same to its children, as far as needed)?

mhuebert16:10:36

one problem with this - from what I understand, the call to om/transact! should not come from a leaf node, hence the “no queries exist” error

mhuebert16:10:07

leaf queries cannot 'stand on their own,' as they only return selectors - '[:leaf/label :leaf/id :leaf/_parent] - the only place where an actual id shows up is at the root node, so every data update would require the whole tree to be reconstructed

dnolen17:10:52

transact! can come from any node that implements IQuery so your conclusion is not correct here

dnolen17:10:17

“standing” on their own also doesn’t make any sense

dnolen17:10:45

the UI is hierarchical but the nothing about the query language specifically is

dnolen17:10:04

a child can always get at some key if you write the parser logic for it

dnolen17:10:11

nothing new here that Datomic Pull doesn’t already do

dnolen17:10:10

that said I also think you’re spending too much time on a fabricated example

dnolen17:10:21

you’d probably get a lot more mileage out of going through the tutorials

dnolen17:10:39

if your UI is nested more than 6-7 levels deep you have serious problems

mhuebert17:10:36

the example is pulled from a little outliner i’m making. you would want to get away from nesting?

dnolen17:10:23

if you’re encoding your UI deeply into the data itself … you’re also creating problems for yourself.

dnolen17:10:06

to me an outliner can and should be a pure stateless component

dnolen17:10:12

you don’t need queries for this stuff

dnolen17:10:55

1) is your component reusable?

dnolen17:10:04

2) yes? don’t write queries

mhuebert17:10:54

so the data itself is just a pile of nodes with child-parent relationships in datascript, which i can use a recursive pull expression to get a tree out

dnolen17:10:20

all this query stuff you’re fighting is completely unnecessary for your problem

mhuebert17:10:03

so you would not use the reconciler/etc at all for this?

dnolen17:10:19

why bother?

dnolen17:10:27

if you have pure component leave it alone

dnolen17:10:43

pure components that the reconciler doesn’t know about are good things

mhuebert17:10:24

and use a listen! on the datascript db and re-render the whole tree on every change?

dnolen17:10:32

“re-render”

dnolen17:10:39

only happens if equality fails

dnolen17:10:58

unless your tree has thousands of things it (which it shouldn’t because React will fall down anyway)

dnolen17:10:04

you should be using windowing

mhuebert17:10:45

so from the first brief reading I’d done I had thought of the indexer as a means to know which components were dependent on which nodes, and then I could force those components to refresh via the :value field in the mutation windowing?

dnolen17:10:15

gotta run, but I cannot explain all the optimizations that React nor Om Next doesn’t solve

dnolen17:10:23

occlusion still matters

dnolen17:10:30

don’t render stuff the user cannot possibly see

mhuebert17:10:16

ok. thanks for the explanations

kahunamoore17:10:41

@dnolen - doc error on http://omcljs.om wiki, page “Components, Identity & Normalization”, in Normalization section: example uses: (def init-date Should be init-data.

dnolen17:10:39

@mhuebert: so put a little bit more thought into on the walk to my office

dnolen17:10:59

so your case actually corresponds almost precisely to the problem of recursion in type systems

dnolen17:10:44

queries are kind of like typing over the UI (this analogy make sense it’s what’s known without running the thing - static)

dnolen17:10:40

@mhuebert: so the question becomes how can the reconciler deal with this thing which has a recursive query

dnolen17:10:43

so this maybe a legitimate use case for very minimal cursors! simple_smile

dnolen17:10:09

so if a query is recursive, switch to a cursor instead (which is purely a runtime thing)

dnolen17:10:08

so all my previous statements hold - but we could provide a way to hook back into the reconciler so you can get precise updates

dnolen17:10:29

not sure about whether I’ll actually pursue that, but I’ve jotted some notes down about it.

mhuebert17:10:55

@dnolen cool, thanks. I can see the analogy w. types & static analysis. I haven’t used cursors in om before so I’ll have to look into that (I used them w/ reagent), I’m not sure what switching to a cursor would look like in this case.

dnolen17:10:46

@mhuebert: it would just be a way to run transact on something that the indexer could not possibly index

dnolen17:10:12

once you hit the recursive case the indexer necessarily would have to give up - it wouldn’t know how deep the thing is or what’s underneath the recursive query

zentrope21:10:21

Are there any examples using the _<attribute> form for reverse lookups?

dnolen21:10:04

@zentrope: that doesn’t have anything to do with Om

dnolen21:10:08

that’s a Datomic thing

zentrope21:10:26

Oops. Sorry, wrong window.

dnolen22:10:48

released Om 1.0.0-alpha10, only changes are more validations and React Native support

maoran23:10:13

@dnolen Hello, I'm reading "Components, Identity & Normalization" and I don't get what the {:keyfn :name} is for in this line (def person (om/factory Person {:keyfn :name}))

jannis23:10:14

@maoran: It's for generating a unique React key based on (:name props), so each Person component gets its own key.

maoran23:10:28

mmm Ok, Thanks =

jannis23:10:38

It's a React thing that it uses to identify components, in particular to optimize/reduce modifications to the DOM.

jannis23:10:50

At least that's my understanding.

nullptr23:10:59

it’s a correctness thing, in some cases

nullptr23:10:24

without a key it just uses order, but if you remove something it needs to know which exact element to remove, for example

nullptr23:10:55

there are other cases where it really doesn’t matter, but i think it’s good to be explicit even in those cases

maoran23:10:43

Ok, understood, and could see this key in the DOM... simple_smile