react

"mulling over state management stuff"
orestis 2020-04-26T00:31:33.099200Z

What’s the bundle size of DataScript? Loading time?

dominicm 2020-04-26T10:35:41.101Z

https://git.sr.ht/~severeoverfl0w/hxadapound I spent the morning assembling this. It's helix, hicada and an atom for a "db". I haven't written "connect" yet, but I think that's the next step. I'm not sure if the use-store hook really makes sense if I am targetting a connect-based workflow. I've inlined a "last rendered" on every component, so that it's clear how it's responding to the state.

orestis 2020-04-26T11:28:24.102Z

I find the hiccup / fn call mix confusing :)

orestis 2020-04-26T11:29:33.103Z

Does it make sense to use context if db is already a global?

orestis 2020-04-26T11:30:22.104200Z

I guess I’d either go full global, or route everything through context (including the ref to db)

dominicm 2020-04-26T11:33:26.105700Z

I need to patch hicada to transform [foo] to what I mean. I want to go full hiccup. Although, the current way looks far more like hiccup. It just doesn't look like reagent.

dominicm 2020-04-26T11:33:49.106400Z

I want to go fully context, I just haven't figured out the details yet.

dominicm 2020-04-26T11:34:24.107Z

Advice on how to do that is welcome. I didn't like the setter/getter method tbh.

orestis 2020-04-26T11:43:03.107300Z

I’m just reading through https://react-redux.js.org/api/hooks

orestis 2020-04-26T11:43:29.107900Z

I wonder if there’s any real downside in wrapping redux

orestis 2020-04-26T11:44:11.108700Z

At the bottom of that doc page they refer to this https://blog.isquaredsoftware.com/2019/07/blogged-answers-thoughts-on-hooks/

dominicm 2020-04-26T12:29:02.109600Z

Redux is much easier to access now, it's not a terrible idea. I'm holding off right now, because I'm not sure if I want to dispatch events or not.

dominicm 2020-04-26T12:29:33.110300Z

I'm going to read those links... after I install an alternative slack client. I have a short screen and can barely read 5 lines at a time in slack 😠 . Sorry OT, but frustrated.

dominicm 2020-04-26T13:26:27.110600Z

https://github.com/aaronc/freactive.core this is probably pretty useful too :) There's a lot of value in working on atoms and allowing something else to manage the lenses on top of that.

orestis 2020-04-26T13:36:09.111300Z

Note when you’re trying to mix atoms and react: https://github.com/Lokeh/hx/issues/41

dominicm 2020-04-26T13:39:55.111500Z

Isn't that more about backing react state with atoms?

dominicm 2020-04-26T13:40:17.111600Z

I think the concurrent mode concerns can be solved with the new hook designed for external state purposes.

dominicm 2020-04-26T14:11:23.111700Z

I've got a very basic connect api up & running for hxadapound now. So you can call connect on a component. It has the exact same API as redux's. Here's an example for the common counter:

(defnc counter*
  [{:keys [count increment decrement]}]
  [:<>
   (last-render)
   [:span (str "Count: " (or count 0))]
   [:button {:type "button" :onClick increment}
    "+"]
   [:button {:type "button" :onClick decrement}
    "-"]])

(def counter
  (connect counter*
           #(hash-map :count (:count %))
           (fn [db]
             {:increment #(swap! db update :count (fnil inc 0))
              :decrement #(swap! db update :count (fnil dec 0))})))

dominicm 2020-04-26T14:12:02.111800Z

Note that there's no longer any global store access & counter* is a pure component, very friendly to previewing in a devcards thing, even if you wanted to play with state.

dominicm 2020-04-26T14:12:26.111900Z

https://github.com/tatut/koukku heh, new wrapper!

dominicm 2020-04-26T14:20:05.112100Z

I like it :) It has a compile-time hiccup with support for a few common elements (for, when, if) in the form of custom keywords. I suppose that does mean it's not suited to wrapping around the whole body?

tatut 2020-04-26T14:50:15.113100Z

regarding koukku, I wanted to do as much at compile time and have the code yield actual react/createElement calls so the runtime layer is very minimal

tatut 2020-04-26T14:51:00.113900Z

that means there's some restrictions, like all components having to wrap their markup in the html macro and can't just return a hiccup vector

tatut 2020-04-26T14:51:07.114200Z

as there's no runtime implementation of hiccup in the library

dominicm 2020-04-26T14:51:14.114300Z

Same goal as hicada :) Compile-time hiccup is an interesting goal really

tatut 2020-04-26T14:51:55.115100Z

I actually converted one of my small reagent+tuck apps to use koukku and it was surprisingly straightforward, the runtime restriction wasn't a big deal

tatut 2020-04-26T14:53:21.116100Z

and the react/useReducer works very well.. just have a multimethod as the reducer, but you do need to pass the dispatch function via some magic, because passing along the fn as props to all children makes them all rerender all the time

dominicm 2020-04-26T14:53:47.116200Z

You can probably pass the dispatch function by using useCallback

dominicm 2020-04-26T14:53:54.116300Z

(unless it has dynamic deps)

tatut 2020-04-26T14:55:21.117400Z

I wanted to refrain from too much magic in the html generating macro, so it won't recurse into user clojure code and rewrite if branches or for body... that's why the common control flow convenience elements are provided

tatut 2020-04-26T14:58:00.118Z

btw, there's a usage example in the repo https://twitter.com/tatut/status/1254323608240914432 using mui

tatut 2020-04-26T14:59:15.118800Z

I think it's nice that the JS React world is coming to functional components as well, I guess there will be lots of stuff in this space

tatut 2020-04-26T15:00:14.119200Z

I think I'll try the useCallback, I missed that the first time around

tatut 2020-04-26T15:19:41.119800Z

no, actually my [some-fn ...args..] form is creating new functions every time, so need to memoize that

tatut 2020-04-26T15:27:26.120200Z

seems the dispatch fn returned by useReducer has stable identity

lilactown 2020-04-26T16:32:36.121200Z

Putting it in context means you can mock it or use multiple global values throughout the tree

orestis 2020-04-26T16:34:56.124100Z

Yeah, I followed up on the next message :) important to keep the whole thing in the context, and don’t use any global references - or forego context entirely!

👍 1
Aron 2020-04-26T17:02:15.124400Z

yes, this is documented explicitly in the react docs

dominicm 2020-04-26T19:15:25.128400Z

Redux's value-add to the world was really in creating a lightweight store abstraction (immutable, notify all subscriptions on change) and then tying it alongside middleware. We could try and integrate with them, but I think the downsides would be: - We'd probably want to invent a way for events to be cljs maps, so basically the use of clj->js as a middleware (not particularly suitable for typing/clicking events due to performance) - middleware makes this possible - We'd not benefit from most of the reducer libraries that exist, if we want to utilize cljs data structures to be our store (I strongly think we should) here's an example of a library which manages to support immutable.js, notice how it uses an alternative entry point to provide specific support https://github.com/supasate/connected-react-router/blob/master/FAQ.md#how-to-support-immutablejs - We could utilize some side effect libs, I picked https://github.com/zslucky/redux-fetch-middleware at random. But it immediately feels like I'm not buying a lot of value, it looks very similar to things like https://github.com/Day8/re-frame-http-fx I don't think we get a lot of the benefit that we would enjoy. React-redux is not massive, 1k loc non-whitespace lines. I think we can capture the core of redux ourselves, without needing to rely on redux directly - which I'm pretty sure duplicates a lot of cljs/gcc core.

Aron 2020-04-26T20:22:26.129300Z

there are many other similar libs like redux (and alternatives like mobx or rxjs) that favoring redux over them would make the objective more about redux than about react

lilactown 2020-04-26T20:41:13.133200Z

FWIW I still think that Redux, like re-frame, is the wrong abstraction. I would like to focus more on building abstractions over domains, rather than super broad things like "global state management." If we think something like redux/re-frame can be used to build higher-level abstractions over those domains, then I see value in them. but IME with both redux and re-frame, the abstraction always seems to stop at the point of redux and some middleware, or re-frame and some effects

lilactown 2020-04-26T20:43:15.134800Z

e.g. if we can ideate an amazing interface for fetching/caching/subscribing to remove data, and it falls out that Redux (or some CLJS equivalent) provides good building blocks for that, then I think it's valuable

lilactown 2020-04-26T20:44:21.135800Z

similarly, a great API for a concurrent mode client-side routing solution might use a global event emitter like Redux, or it might not.

lilactown 2020-04-26T20:47:49.138700Z

trying to leverage a monolithic global state management solution IME ends up being a lowest-common-denominator API that feels difficult to boil away. it's telling that a lot of high level libs that started out building on top of redux, are either fairly unpopular or eventually removed redux because it was too opinionated for consumers

Aron 2020-04-26T20:48:08.138800Z

I 100% agree, but I am taking soo many contrarian positions on this slack that this was a battle I didn't really want to join 🙂 Glad that there are others who think similarly!

Aron 2020-04-26T20:50:44.140900Z

I like the idea of single source of truth, but I have this suspicion that people misunderstand it. The single source is not about ALL state in an app, but ANY state. Which means that different parts of the state might have different single sources. In fact, it's much more easy to dismantle highly derived state if one takes this approach because now the logic that manages said state can take this role, instead of having actions/effects tied to components or apps. It can even move outside.

👍 1
dominicm 2020-04-26T21:03:03.141800Z

@lilactown are you thinking of things like a data synchronization library?

lilactown 2020-04-26T21:04:10.142200Z

depends on what you mean by > data synchronization

dominicm 2020-04-26T21:06:49.143700Z

I guess the act of fetching data, and sending changes back to somewhere else. Everything in the range of datahike, firebase, or REST calls based on a schema.

lilactown 2020-04-26T21:12:00.145100Z

I can't wrap my head around the problem of an e2e solution like datahike/firebase yet, but something on the level of: • fetching data • suspending based on query • optimistic updates on mutation are what are top of my mind lately

dominicm 2020-04-26T21:13:12.145500Z

What is "suspending based on query"?

dominicm 2020-04-26T21:20:13.146200Z

Some of what you're describing reminds me of dnolen's work on om.next

lilactown 2020-04-26T21:21:47.147400Z

if you look at react's page on data fetching with suspense

lilactown 2020-04-26T21:24:37.150Z

they sort of develop a pattern: • an action starts a request (e.g. navigation, button press, etc.) • a related render is triggered, which reads from the cache and suspends • when request has completed, re-render with data this makes sense when you think of it in the scope of a single request. but often, the apps that I've built are fetching and populating different pieces of domain info (e.g. a particular user's information) ad-hoc

lilactown 2020-04-26T21:27:58.151800Z

so when I'm developing a component, what I would like to do is subscribe to the domain info I need, and define the behavior which should suspend the component, irrespective of the exact request that ends up triggering that behavior or updating that domain info

lilactown 2020-04-26T21:37:32.152100Z

if that makes any sense 😛

Aron 2020-04-26T21:56:01.153Z

And I am just simply suspicious of this idea that the render, that is the View should trigger some IO. The view/component depends on the data, declaratively, not the other way around

lilactown 2020-04-26T21:57:29.154200Z

I agree. the pattern I'm describing would encourage you to separate fetch from render, instead render queries the cache and decides whether to suspend until data is ready

Aron 2020-04-26T21:59:34.156200Z

Often, the app looks/design/content changes at the same time the API changes, if the renders need to handle both, the complexities get entangled. With separated fetch (I like to call these services, like ServiceWorker) the two can be maintained entirely separately. This is why I want to use the same query language internally as externally, but often backend developers are not very enthusiastic about adopting datalog or even transit 🙂

👍 1