helix

https://github.com/Lokeh/helix
dominicm 2020-04-23T19:45:27.333300Z

Just reading https://github.com/reduxjs/react-redux/issues/1351 in reference from "what about hiccup", and mentions of concurrent mode. It definitely seems to me that an immutable store like clojurescript could provide would solve tearing issues with externals

lilactown 2020-04-23T19:46:06.333800Z

it doesn’t if you always read from the external store in each component

Aron 2020-04-23T19:50:55.334400Z

redux is buggy (imho, don't take it as an attack on them please).

lilactown 2020-04-23T19:56:26.338800Z

the tearing problem occurs if components read from an external store in an ad-hoc way. Imagine this:

(def store (atom {:name "lilactown"}))

;;
;; start rendering the component tree, components access store
;;

@store
;; => {:name "lilactown"}
@store
;; => {:name "lilactown"}

;;
;; Rendering is paused for some reason (suspension, or a higher priority update comes in)
;; Meanwhile, the store is altered
;;

(swap! store assoc :name "dominicm")

;;
;; Now we resume rendering
;; We re-use the components that were already rendered using the old value,
;; but the components that were left over will get the latest value - tearing
;;

@store
;; => {:name "dominicm"}
,,,

dominicm 2020-04-23T19:58:15.339600Z

@lilactown what would not be ad-hoc, but would be external state?

lilactown 2020-04-23T19:58:38.339800Z

I don’t know 😄

dominicm 2020-04-23T19:59:04.340400Z

Haha 😄

dominicm 2020-04-23T19:59:52.342200Z

It would be nice to have some way to control access to external state in a way that allows us to use snapshots or versioning. Obviously, this is very through the lens of clojure.

lilactown 2020-04-23T19:59:55.342300Z

there would have to be some way to capture the state of the external store at the moment of render, and ensure that that value is used consistently throughout the component tree

lilactown 2020-04-23T20:00:34.342900Z

there’s an experimental hook called useMutableSource that supposedly handles this, I have not tried it

lilactown 2020-04-23T20:00:41.343100Z

it has its complexities tho

lilactown 2020-04-23T20:00:47.343300Z

https://github.com/facebook/react/pull/18000

dominicm 2020-04-23T20:01:44.343800Z

Reading the rfc, the introduction looks perfect though

dominicm 2020-04-23T20:02:51.344200Z

Seems like it could easily add a watch to an atom.

dominicm 2020-04-23T20:03:47.344800Z

Oh, the rfc explicitly mentions redux. Hmm. That's cool.

dominicm 2020-04-23T20:05:21.346700Z

@lilactown given this, and the alpha nature of suspense, I feel confident that you could avoid the cascading problem that's mentioned in https://github.com/Lokeh/helix/blob/master/docs/faq.md#what-about-hiccup with the changes that are coming down the pipeline.

lilactown 2020-04-23T20:06:44.347200Z

yeah it’s very promising. I don’t know what the tradeoffs are yet by using useMutableSource for all state

lilactown 2020-04-23T20:06:58.347500Z

hopefully the Redux peeps will exercise that 🙂

lilactown 2020-04-23T20:07:58.348800Z

runtime hiccup parsing will always be strictly slower, though, and I feel pretty good about the ergonomics of helix’s current approach to element creation using the $ and helix.dom

dominicm 2020-04-23T20:08:12.349500Z

One problem I have is that they haven't exposed a way to describe equality. So a counter may be a good tool.

lilactown 2020-04-23T20:08:25.350100Z

I hope that if people want to add hiccup, they can do that a la carte

dominicm 2020-04-23T20:08:58.351200Z

Oh, absolutely. No denial there. I'm just trying to figure out just how bad I'm making things for myself :)

😄 1
lilactown 2020-04-23T20:09:32.352700Z

I think that the getVersion would just use reference equality

dominicm 2020-04-23T20:10:01.354Z

What I need to figure out next is: how do I want to manage state? I don't like reframe, so I'm in no rush to clone it with hooks. But I'd also love to use someone else's work, if it existed...

lilactown 2020-04-23T20:10:02.354100Z

the benefits to immutable data is that you can rely on a “fast no” - if the reference changed, the data probably changed!

dominicm 2020-04-23T20:10:22.354600Z

Yeah, I guess I don't often do swap with identity!

lilactown 2020-04-23T20:11:23.355500Z

the time this doesn’t work is when you’re dealing with external data e.g. data serialized from an HTTP request. if the data is the same as before, and you swap it into your store, then it can trigger a meaningless re-render of your app

lilactown 2020-04-23T20:12:13.357200Z

but also, using = would have to do a deep equality to ensure it was not changed, which might be bad for perf reasons

lilactown 2020-04-23T20:12:20.357500Z

it’s all too hard!!

dominicm 2020-04-23T20:12:41.358Z

I'm assuming we can get this working with cursor functions too. But I also think that a counter that increments on non-equality could work out as a version.

lilactown 2020-04-23T20:15:45.358800Z

= would also fail for things like dates etc.

aisamu 2020-04-23T20:16:20.359600Z

> I don't like reframe Sorry if it's a bit off-topic for this channel, but could you elaborate?

dominicm 2020-04-23T20:16:32.360100Z

Perfect is the enemy & all that 🙂 Just needs to be "good enough".

lilactown 2020-04-23T20:16:44.360400Z

it’s probably better to do the check of whether you should update the store, at the swap level - e.g. have some check that determines whether you should update the store given the current state

Aron 2020-04-23T20:17:05.361Z

https://twitter.com/dan_abramov/status/691301224541503488 you can just call render with full state, no problem 😉

lilactown 2020-04-23T20:17:17.361400Z

re: managing state, I’ve just been using local state mostly.

lilactown 2020-04-23T20:17:57.362200Z

the only thing you really want an external store for are things like routing, external data cache, and maybe a few other select things like device status

lilactown 2020-04-23T20:18:44.363600Z

for the majority of your UIs state, local state is way simpler and way easier to move around/test/understand performance of

Aron 2020-04-23T20:20:06.365100Z

routing on the frontend is an antipattern, but I personally love shared state. I built so many websites and what inevitably happens is that some state local to some component need to be accessed from some other component. I rather have a global datascript db and just do queries for react views and pass them down. I mean for small things, local state is 100% adequate, but once you get into heavy duty client side things with lots of data and especially stuff that needs to be synchronized, using local state quickly gets messy

dominicm 2020-04-23T20:22:20.367Z

@lilactown RE local state, how do you handle many things needing the same state? e.g. fetching data about logged in user. I'm fine with local state for things that are truly local, but it seems like I'm missing things like hot reload (I know about fast refresh). Ideally I'd have a fairly "pure" set of components on the whole, most of them taking their arguments as props.

lilactown 2020-04-23T20:23:02.367700Z

yeah caching data is the biggest thing I’m focused on solving at work right now

lilactown 2020-04-23T20:23:33.368400Z

we have a 50k LOC re-frame/reagent app that we are building new features in helix now

lilactown 2020-04-23T20:23:51.368900Z

the only thing we’re currently actively still building in re-frame is data caching

lilactown 2020-04-23T20:24:11.369400Z

I’m trying to figure out the best way to handle that without as much re-frame fanfare… it’s quite complex

lilactown 2020-04-23T20:25:09.370500Z

re-frame’s event system is actually a pretty poor abstraction for handling processes like fetching data about a thing, because you have to handle so many branching logic paths that are scattered across events

lilactown 2020-04-23T20:25:54.371100Z

fetch/cancel/complete/fail, and often you want to chain them

lilactown 2020-04-23T20:26:23.371600Z

events are extremely low level for that kind of thing

dominicm 2020-04-23T20:28:59.372100Z

:thinking_face: So you're trying to simultaneously redesign & replace re-frame? 🙂

dominicm 2020-04-23T20:29:17.372500Z

But yeah, that's one of my big complaints. Scattering.

lilactown 2020-04-23T20:31:39.373Z

my goal atm is to figure out what a better abstraction is

lilactown 2020-04-23T20:31:59.373500Z

then build the abstraction to interop with our current re-frame machinery

lilactown 2020-04-23T20:32:14.373900Z

and eventually probably replace re-frame with something simpler

lilactown 2020-04-23T20:32:30.374100Z

I’m still at step 1 tho 😛

lilactown 2020-04-23T20:33:24.374700Z

but a good example is like, apollo’s graphql client basically obviates the need to use re-frame as the data cache if you’re all GraphQL

dominicm 2020-04-23T20:34:48.375500Z

Yeah. I should perhaps investigate that. I did get quite excited by https://github.com/forward-blockchain/qlkit for a while. I do like the idea of having each component declaring it's required data and having that handled structurally as if by magic 🙂

1
lilactown 2020-04-23T20:35:55.376Z

if I had all the free time I would investigate pathom as a clojure-ific approach to it all

lilactown 2020-04-23T20:36:02.376200Z

but there are some decently hard problems

lilactown 2020-04-23T20:36:31.376900Z

fulcro provides a component-centric view of declaring external data requirements, but it also handles all state that way which I don’t want

dominicm 2020-04-23T20:37:08.377900Z

I also think pathom & eql is huge in scope, learning all that is not too appealing to me. I've got stuff to do.

lilactown 2020-04-23T20:37:15.378100Z

I want someone to add a lib to their helix project that gives them all the querying flexibility of pathom with an excellent caching/subscription behavior, just like apollo

👍 2
lilactown 2020-04-23T20:37:38.378600Z

yeah, there’s a lot of internal complexity to it that requires investment

lilactown 2020-04-23T20:37:54.379Z

lots of new terms to learn (took me like 3 days to figure out what an “ident” was) and setup required

dominicm 2020-04-23T20:39:19.380Z

fwiw, I think your goal above sounds wonderful! I would add the constraint that it should probably work with any competing libraries (hx/uix?) too. It would be nice to see less "re-" in reframe

dominicm 2020-04-23T20:40:03.381Z

One confession I have is that I've currently hooked up a global "user" in an atom using https://github.com/wavejumper/rehook But I'm not sure if it'll stick 🙂

dominicm 2020-04-23T20:41:14.382300Z

The good news is that it allowed me to remove a window.reload() that existed due to the root component reading the account-type once on start...

🙌 1
lilactown 2020-04-23T20:42:04.382900Z

I mean, Concurrent Mode / Suspense for data fetching / etc. is probably not coming out soon. I would be elated if it came out in 2020, but not expecting it.

lilactown 2020-04-23T20:42:13.383200Z

we all gotta get work done.

lilactown 2020-04-23T20:42:27.383600Z

that’s why I’m not ripping out re-frame any time soon on our legacy app 😂

dominicm 2020-04-23T20:43:17.384100Z

I think the below captured it well. There's a lot of indirection introduced by a single high-level action. e.g. the code for getting the current user ends up in 5 places: • initial event that triggers it (click) • subscription to eventual data • user event that dispatches to http event • http event success • http event failure Doing all that every time I want data is a lot of boilerplate and work to juggle all the locations. Dependent on how/what you're testing, you may not feel the benefit of all that work.

👍 1
dominicm 2020-04-23T20:43:57.385700Z

Yeah, my "legacy" is small enough that I'm able to rewrite it onto whatever I want... I'm just not sure what I want!

Aron 2020-04-23T20:44:09.386300Z

am I too much a contrarian if I say that React should just dispatch actions and all state management should be outside of it? 🙂

dominicm 2020-04-23T20:44:12.386400Z

I'm just happy to be rewriting JavaScript into Cljs.

Aron 2020-04-23T20:44:33.386900Z

something like a serviceworker

lilactown 2020-04-23T20:44:52.387500Z

I think the goal should be to find an abstraction that provides a good enough interface between React and external state that it makes it mechanically simple to migrate to suspense or something CM-safe later

dominicm 2020-04-23T20:45:04.388Z

https://github.com/active-group/reacl this is pretty on topic to our conversation really 🙂 Your comment makes me think of that @ashnur I think it could be used both ways.

Aron 2020-04-23T20:45:40.388600Z

thanks! I am checking the links you share, but I am also working, so it takes a bit of time

lilactown 2020-04-23T20:46:28.389400Z

I think it’s nice in theory, but it just moves the problem around. the benefits to allowing React to control your state is that it can prioritize it appropriately using concurrent mode

dominicm 2020-04-23T20:47:31.390400Z

@lilactown You're right, but I think you can have both. Unless there's something I don't know about concurrent mode? Do they have some kind of prioritized queue for async state updates...?

lilactown 2020-04-23T20:47:42.390600Z

yes, they do

dominicm 2020-04-23T20:48:23.391700Z

Interesting. I wonder if there will be a point at which users can participate in prioritization... very likely given that redux is a priority.

lilactown 2020-04-23T20:49:09.392200Z

yeah probably, there’s an experimental package https://github.com/facebook/react/tree/master/packages/scheduler

lilactown 2020-04-23T20:50:14.393500Z

but there’s complexity that React handles for you by tying your state update to rendering to help avoid tearing 😄

dominicm 2020-04-23T20:50:36.393800Z

I have some confidence in the react team making it possible to achieve things based on whatever API I want 🙂 I have to trust them about what they say is good in principle.

dominicm 2020-04-23T20:51:10.394700Z

e.g. they're not (as far as I can tell) anti-external state. They want to support it. So I don't have to care. I'll just build whatever and make it fit with the advanced api they provide me for supporting concurrent mode.

lilactown 2020-04-23T20:55:39.395400Z

probably the best approach

aisamu 2020-04-23T22:13:48.398700Z

like https://github.com/den1k/subgraph ?

Aron 2020-04-23T22:14:06.399300Z

https://clojurians.slack.com/archives/CRRJBCX7S/p1587674788389400 I believe that react's scheduler/reconciler can perfectly well integrated with global state. I've read a lot of Fiber's source and a lot of discussions on github and I don't see any reason why global state couldn't be as performant as local state.

Aron 2020-04-23T22:14:10.399600Z

especially with fiber

lilactown 2020-04-23T22:14:51.400300Z

looks nice, but something not depending on re-frame

Aron 2020-04-23T22:14:59.400700Z

first and foremost, basic state update triggers are the lowest priority afaik in concurrent mode, so just doing the naive approach already gives you most of the benefits

Aron 2020-04-23T22:19:02.401400Z

there is some new event system that's incoming too

aisamu 2020-04-23T22:19:38.401500Z

Yup, that's "just" MapGraph on "top" of re-frame. But if you don't use the *.re-frame machinery it still works. The reactive part comes from reagent's reactions, though

Aron 2020-04-23T22:24:26.401900Z

cant find it now but https://github.com/reactjs/rfcs/blob/master/text/0147-use-mutable-source.md

Aron 2020-04-23T22:25:08.402400Z

isn't this exactly what you expect @dominicm?

dominicm 2020-04-23T22:25:41.402600Z

Yeah, I was linked that above 🙂

Aron 2020-04-23T22:30:22.403100Z

sorry, I am slow > Both require a memoized “config” object with callbacks to read values from an external “source”. these could be the static queries I mentioned

Aron 2020-04-23T22:35:39.403300Z

wait, I just realized this is merged