Fork me on GitHub
#re-frame
<
2015-10-26
>
mikethompson01:10:32

@jelz has done some fine work creating a re-frame version of the flux-challenge. If any of you experienced re-framers have time, please give feedback: https://github.com/Day8/re-frame/issues/126 (I've asked jelz to come here for that feedback, but I'm not sure what his circumstances are, and if that is possible).

mikethompson01:10:55

It looks like a fun little exercise (I wish I had more time for it right now)

escherize05:10:32

I'm guessing there's a better way to center something horizontally:

(defn center [form]
  [rc/h-box
   :size "initial"
   :children [[rc/box
               :size "auto"
               :child " "]
              form
              [rc/box
               :size "auto"
               :child " "]]])

mikethompson06:10:23

First adjustment might be (untested):

(defn center [form]
  [rc/h-box
   :size "initial"                     ;  needed ???
   :children [[rc/gap :size "auto"]
              form
              [rc/gap :size "auto"]]])
But an easier way might be ...

mikethompson06:10:32

(defn center [form]
  [rc/h-box
   :size "initial"                       ;; needed ?
   :justify   :center                 ;; <----- new
   :children [[form]]])

mikethompson06:10:52

Summary: look at the justify parameter. Don't trust the code above ... its been a while since I had to play with re-com so I might be forgetting something.

jelz08:10:18

Hello everybody, I believe that I've already been mentioned here: https://clojurians.slack.com/archives/re-frame/p1445822732000398 I've just updated the ticket - as I stated there, it would be great if you could take a look and comment in this pull request.

jelz08:10:00

The idea is to transform my code from "exercise of Clojure newbie" level to something that can be representative for re-frame simple_smile

kurtlazarus14:10:12

Has anybody successfully used devcards with re-frame? I ask because I am attempting to use devcards to demonstrate small, simple examples with re-frame. I thought I’d do this with via dependency injection. However, this is turning out a bit more difficult than I thought.

kurtlazarus14:10:08

I’d like to be able to inject my own database if I need to. However, there’s no hooks (is that the right term?) for me to do so.

kurtlazarus14:10:34

I’d love to hear people’s ideas. Forking could be an option.

darwin14:10:43

@kurtlazarus: you can dynamically replace re_frame.db.app_db as a quick hack, if you want more flexible approach, look at my fork: https://github.com/darwin/re-frame, with that you can implement something similar to scaffold.cljs on your side

kurtlazarus14:10:50

@darwin: Thanks, I’ll take a look.

darwin14:10:50

I’m also considering re-writing scaffold.cljs in a better way, without app-db and app-frame as global state, then it would be easier to reuse bits from there, just didn’t get to it, something along Stuart Sierra’s recommendations in component readme

darwin14:10:23

"Encapsulate all the runtime state needed by the library in a single data structure. Provide functions to construct and destroy that data structure. Take the encapsulated runtime state as an argument to any library functions which depend on it."

kurtlazarus14:10:30

Are there any other relevant pull requests?

kurtlazarus20:10:14

@darwin: @tel I figure it would be good to pull the conversation in here.

kurtlazarus20:10:46

I could see different run loops getting messy.

kurtlazarus20:10:23

Maybe it would be something similar to Elm’s/ReactiveX’s philosophy?

darwin20:10:52

why? each is its own isolated world which does not know anything about others, I think messy would be to have just one run loop and try to keep handlers and subscriptions for all devcards in one re-frame instance

darwin20:10:12

I’m not familiar with Elm/ReactiveX

tel21:10:38

@darwin: that’s pretty much exactly what I did (re Stuart Sierra’s component stuff)

tel21:10:50

in fact my boot/teardown from figwheel is exactly using component

tel21:10:12

as I noted in the cljs channel, the tricky bit is in "Take the encapsulated runtime state as an argument to any library functions which depend on it."

tel21:10:39

because it’s a little tough to actually get access to that state in the render tree

tel21:10:46

but can be done with react child context

darwin21:10:19

@tel: I know, just rewriting plastic to component model, it is a huge stuggle

tel21:10:00

oh, you’re going through a full rewrite there? wow

darwin21:10:32

didn’t have that much problem passing context to reagent components I pass the context as normal param to render function

tel21:10:02

you just pass it all the way down from the top?

darwin21:10:21

the problem is to propagate the context to deepest functions on call stack

darwin21:10:50

almost all functions get this first “context” parameter and have to pass it further down

tel21:10:52

yeah.. you really want a nice reader monad, but you can’t really get that in clj

tel21:10:11

but you can fake it with react context

tel21:10:56

I have a macro (tokamak/with-reactor rx …) which “pulls” the context data structure from thin air (actually the react child context)

tel21:10:27

it’s a little unfortunate though because it creates a new react component just to capture the “reactor"

tel21:10:48

but it doesn’t translate to actual DOM, so the bottleneck should be minimal

darwin21:10:33

hm, interesting, but I don’t want to go down to react level if possible, I could do something like this using clojurescript tools

tel21:10:47

clojurescript tools?

darwin21:10:03

I mean clojurescript primitives

tel21:10:04

I agree that going through react is a real pita

tel21:10:27

I don’t know if that’s possible… you’d have to hack it into reagent or react

tel21:10:35

maybe you could do it with reagent

tel21:10:44

but I’m not as familiar with reagent internals

darwin21:10:49

I already have this, so I could bind some global in run-queue here, and pull it when needed: https://github.com/darwin/plastic/blob/master/src/main/plastic/reagent/patch.cljs

tel21:10:20

does render queue capture every instance of calling the reagent render fn?

darwin21:10:05

I’m not sure now

tel21:10:08

I originally tried doing this using (binding [*rx* …] (r/render …)) but that meant that the reactor was only bound during the initial render

tel21:10:27

thus needing to shove the state into the react component tree

tel21:10:41

but I would love to just bind it once

tel21:10:57

then components can pretend to have global state

tel21:10:13

but the person who actually renders the components will get final say in which reactor is in use

tel21:10:23

how are you patching the render queue in?

tel21:10:31

can you do it on a per-thread basis?

darwin21:10:48

well, this is js

darwin21:10:59

what threads do you mean?

tel21:10:20

as in per r/render call

tel21:10:43

not exactly thread, but that execution context

tel21:10:49

however it’s implemented

darwin21:10:05

I was probably wrong, this run-queue does only updating of components which were marked as dirty

darwin21:10:17

render functions ran reactively before

darwin21:10:32

so I take my words back

darwin21:10:01

another idea: if you have just one app-db atom, you could set that context before doing reset!/swap! on that atom

darwin21:10:31

reactions run from watchers on atom, so having that context set by that time should work

darwin21:10:28

in case of official re-frame, you could do it in pure middleware or in your own run-loop

tel21:10:28

the challenge, I think, is when a new component gets rendered it can’t know where to pick up the global db reference from if it needs it

tel21:10:45

“global"

tel21:10:19

haha, I should really just figure out how to do at least a beta release of this stuff instead of bantering around so vaguely

darwin21:10:23

well, initial render is done after initial mount, isn’t it? so you would do the same thing before mounting

darwin21:10:10

dunno, as I said, I’m passing the context all the way down as first parameter, so this is not really my problem ATM

tel21:10:30

tbh, I was super tempted to just do it that way as well

tel21:10:51

it somewhat dramatically simplifies the whole game

tel21:10:21

(although here’s a great place to really wish for monads)

darwin21:10:44

my problem right now is having two re-frame instances, one running in main js context and a second one in a web-worker js context, they can dispatch events to each other, but sometimes need continuation, for example main thread asks for some work on worker thread and when that thing is about to be committed to worker’s app-db, it wants to be notified about results and finish some work on main thread before updating main app-db. basically I need both app-dbs to be in consistent state versus each other (eventually). so I can implement undo/redo on top of it.

darwin21:10:48

maybe having full re-frame running in worker context was an overkill

darwin21:10:57

still trying to get my head around that

tel21:10:46

I guess the worker can’t dispatch on the main

tel21:10:26

oh, so perhaps I misunderstand, why not just have it throw back to the main when it’s done working?

darwin21:10:50

usually, sometimes main thread just asks worker to update its state without answer back

darwin21:10:10

worker is statefull, it is not just do some pure work and give me back results

darwin21:10:25

worker keeps whole parse-tree and does analysis and layouting

tel21:10:53

could the worker state just live within a sub-region of the main state?

darwin21:10:53

main thread does renders it via reagent and manages some lightweight state, like cursors and selections

tel21:10:27

you could use some kind of lens to focus in on the substate for the worker

tel21:10:40

and transform the edits it does

tel21:10:10

then as long as the main app has a read only stance w.r.t. that substate it shouldn’t trample anything

tel21:10:23

I guess the serialization boundary makes that not work, though?

darwin21:10:30

no, I’m afraid, it can’t easily, I cannot marshal whole data structure over the messaging channel

darwin21:10:02

I will just have to be careful and keep good track of which pairs of app-db are suitable for undo snapshot

darwin21:10:31

for example when both run-loop got empty channels, that point is fine

tel21:10:35

you could maybe replicate the state transducer on each side

tel21:10:51

and have the main app tail the worker’s “op log” that it dispatches back to main

tel21:10:04

but.. that’s a bit messy, haha

tel21:10:11

and depending on where the work happens could totally negate the purposes of the webworker

darwin21:10:00

well, I have a debug mode which runs both in main js context (optionally without marshalling)

darwin21:10:07

that is why I had to rewrite re-frame 😉

tel21:10:33

haha, yeah

tel21:10:38

makes a load of sense

tel21:10:10

I think the pure state transformer bit could be extracted as well, which could be sort of appropriate for web worker situations

tel21:10:21

though things like undo-redo wouldn’t be relatable anymore

tel21:10:39

but there is a lot of structure around plain old state machines

darwin21:10:12

thanks, I strongly believe someone already tackled something like this and there is a good theory about it somewhere

darwin21:10:18

maybe I should not do undo snapshots on worker at all, undo on main thread should “cause” some events to be fired on worker, to get back to some relevant state to match main app-db

darwin21:10:22

something like that

tel21:10:56

you could maybe see the worker as a subscription on the main state

tel21:10:10

which stores local state for efficiency purposes

tel21:10:43

in which case annihilating it all and rebuilding upon world changes like undo/redo feels reasonable

tel21:10:44

I guess that really depends a lot on what undo/redo functionality means for your app

tel21:10:11

that’s another thing which always feels a little weird about re-frame since it’s global… the undo/redo effects are certainly “outside” of the application state

tel21:10:26

it’d be nicer if it could all be hierarchically nested

darwin21:10:52

yeah, maybe doing undo/redo by snapshotting app-db is not the right approach

darwin21:10:21

for example in my app-db I keep state of multiple code editors, it makes little sense to do undo on whole app-db

darwin21:10:26

I want undo on editor state

tel21:10:34

you could just store the zipper of states itself

tel21:10:50

and modify the transducer to conj new states

tel21:10:59

and even discard some at times

tel21:10:34

{:editor-states [{:text “la”} {:text “lal”} {:text “lala”}]}

darwin22:10:07

that is what I have now, I do complete snapshot of editor state and store it as vector

tel22:10:31

(defn historical [redu] (fn [state ev] (let [new (redu (get state 0) ev)] (conj state new)))

tel22:10:01

a transducer lifting a normal reducer to produce a trace of states

darwin22:10:42

what makes better sense, is to prepare undo event, for each mutation event, but this is a lot more work and prone to breaking

tel22:10:23

prepare it how?

darwin22:10:08

well for each mutation event, you can build inverse event, “insert text” -> “delete text”, “change color from x to y” -> “change color from y to x"

tel22:10:42

yeah, that seems a lot harder

tel22:10:56

you work with a theory of diffs instead of a theory of events

tel22:10:12

gets you one step toward collaborative editing though simple_smile