Fork me on GitHub
#helix
<
2021-07-21
>
kennytilton00:07:24

tl;dr Reagent has glitches, Matrix does not. In Matrix, where a single state change cascades to other state changes, the usual case, these all occur at the same logical time. React will like this, because a single triggering change will result in all consequent set-state calls being made in bulk. longer answer: In general, before addressing the integration with RN, Matrix is more evolved than Reagent. State propagation does not happen immediately, and does not happen in an uncoordinated fashion. So, eg, no glitches as with Reagent. A single state change propagates fully before the next is dispatched, and no code sees stale data that might be awaiting propagation -- everything computes JIT if accessed before the dumb propagation sweep gets to it. Matrix is 25 years old and has been thru a lot, and made it to version 3 before I saw a glitch utterly break an app and I addressed the problem. As for React/RN integration, I hope to make peace with React and associate a generic state hook with every component. Then, when Matrix sees a state change on any property of a component, it will do a (set-state {}) to trigger the rerender. This should be efficient even if at the top of a hefty tree because the underlying tree will "know" thanks to Matrix state information that it is unaffected by the state change and just return itself as it was before the state change. Well, state is all connected so there will be scattered change throughout the tree, but, unlike with Reagent, a precise change orchestration will effect the several changes cleanly. MobX and Binding Scala are other state machines that avoid glitches. I dug into MobX and it uses the same JIT approach to state re-calculation, while using a different mechanism altogether to identify staleness. I digress. This is not as good as my Web version, which selectively updates the DOM with "point" granularity; we will still go thru the React VDOM b.s., but we will not be slower than the typical React app, and we will remain compatible as React evolves. More or less. :)

kennytilton00:07:12

The problem with React state management is that they never came up with a good solution. I have followed them from the beginning, when context was there but its use denigrated. But the whole lifting thing and the need to chain callbacks down thru props -- well, that is why context is back and downright recommended. But context is still manual wiring, and folks like Matrix and MobX offer transparent state management, so ReactJS is kinda doomed state management-wise. Btw, even ReactJS recommended the Flux model as a solution to its own state management scheme, and Redux's popularity arises from the inferiority of React state management, even though Redux is a PITA of boilerplate. CLJS re-frame chose to echo the Redux scheme, and does a nice job for those who do not mind building the state DAG by hand.

kennytilton01:07:35

You might consider Hoplon/Javelin. That achieves reactivity transparently, though in very limited ways, one limit is that it works by source inspection and misses the specifics of dependency possible only dynamically at run time. But the transparency is way cool.

wilkerlucio02:07:03

@hiskennyness there is also a the new Fulcro Raw facilities, which you can use as react hooks, it provides a robust normalized state management https://book.fulcrologic.com/#_fulcro_raw_version_3_5

wilkerlucio02:07:12

kinda like the Apolo of CLJS

kennytilton04:07:29

Wait @wilkerlucio!? Fulcro rolls its own statemanager?! runs to look

kennytilton10:07:48

@wilkerlucio Hmmm, Fulcro has full stack data management, but is that the same as state management in the MobX or Reflux or re-frame sense? Here is the definitions from the Cells Manifesto: "Cells is a mature, stable extension to CLOS[impl] allowing one to create classes whose instances can have slot values determined by instance-specific formulas. "When application code assigns to some input cell X, the Cells engine guarantees: -- recomputation exactly once of all and only state affected by the change to X, directly or indirectly through some intermediate datapoint. note that if A depends on B, and B depends on X, when B gets recalculated it may come up with the same value as before. In this case A is not considered to have been affected by the change to X and will not be recomputed. -- recomputations, when they read other datapoints, must see only values current with the new value of X. Example: if A depends on B and X, and B depends on X, when X changes and A reads B and X to compute a new value, B must return a value recomputed from the new value of X. -- similarly, client observer callbacks must see only values current with the new value of X; a corollary: should a client observer SETF a datapoint Y, all the above must happen with values current with not just X, but also with the value of Y prior to the change to Y; -- and finally, deferred “client” code must see only values current with X and not any values current with some subsequent change to Y queued by an observer -- https://tilton.medium.com/the-cells-manifesto-b21ed10329f0

kennytilton11:07:57

I do see vague overlap with MobX/Matrix state management and "push" or subscriptions in elaborate data management stacks such as GraphQL, but it is not clear that state managers have anything to add to data managers.

lilactown15:07:28

I understand how matrix/cells avoids "glitches" in the FRP sense, but it sounds like it will still have to reckon with the same problem that I was talking about in my blog post you linked above: since you're storing state outside of the tree, React cannot re-prioritize or slice up computations across event loop ticks as effectively

lilactown15:07:58

not saying that reagent or matrix don't work. I think that there's some experimentation required to really dive in to how this effects the UX of apps built with them and how to manage the tradeoffs of leveraging time slicing & concurrent mode vs. having a unified way of managing state

kennytilton15:07:38

(a) I am not impressed by the React team and their talk of hyper-super-optimization, most of which they conceded is vaporware; but (b) I am using the state hook just as I would if I were manually wiring up the app. I mean, React does not know when anyone will call setState with what data. So I do not see what I am hiding from React.

lilactown15:07:10

does the source of truth for the data that matrix manages live outside of the useState hook?

lilactown15:07:04

i.e. if I wanted to inspect the state of a cell from a REPL, how might I do that?

Jimmy Miller17:07:17

> since you're storing state outside of the tree, React cannot re-prioritize or slice up computations across event loop ticks as effectively I found this thread (and accompanying link about tearing) to be pretty useful in understanding this https://github.com/reactwg/react-18/discussions/70

kennytilton17:07:43

The useState hook sees nothing; I am just calling (set-state {}) when I know the rendering function needs to be called. Apparently that is enough. If not, the Cells mechanism relies on an increasing so-called bigint *pulse* and that is guaranteed to be different when something has changed and requires a re-rendering. As for inspecting a cell from the repl, are you running one of the examples? I'll go look in the meantime...

kennytilton17:07:30

tiltontec.model.core/matrix is a gobal to which is bound an atom holding the root Mx instance. `(tiltontec.model.core/md-cz @matrix) will give you a map of the cells that control properties of that Mx instance. Each is an atom contining a CLJS object that constitutes a cell)

kennytilton08:07:18

I got to thinking and realized that, since my state management scheme is so awesome 🙂, I could indeed sneak all the model data into one place and then have all the code read from that one place. But this is what happens when we try to live with a library dictated by Facebook engineers: we end up all twisted out of shape and struggling to accomplish what should be trivial. No thanks. I will bet that, by the time they make Concurrent Mode mandatory (if ever), there will be cross-platform native alternatives. FB has renounced reactive systems explicitly, saying "we want to be in control". But looking at the pinball trajectory of React state management...good friends don't let FB engineers drive state management.

kennytilton23:07:41

btw, consider for a moment the sad arrogance of FB engineers. In the beginning they tried gloating over how React is just about the view. Now they want all application state within React. Their design sense is nil, and they are the pied piper leading thousands of teams into Hell. I hope ClojureDart is making headway!

lilactown17:07:56

I'm asking questions to understand high level, I don't currently have the time to run the examples

lilactown17:07:18

> The useState hook sees nothing; I am just calling `(set-state {})` when I know the rendering function needs to be called. Apparently that is enough. this has the same tearing issue I was trying to talk about in my blog post you linked

lilactown17:07:59

again, not chastising, just trying to make clear my point in the blog post since it sounds like it may have gotten lost in translation

lilactown17:07:12

calling (set-state {}) is not enough to prevent tearing

lilactown17:07:25

(when using concurrent mode)

kennytilton17:07:08

"I don't currently have the time to run the examples" That's fine, I just not was sure how best to answer your question.

2
lilactown17:07:35

I think my question was answered by > The useState hook sees nothing; I am just calling `(set-state {})` when I know the rendering function needs to be called. Apparently that is enough. I was asking the REPL question because if you can inspect the state of a cell from a REPL easily, then it's probably not stored in the React tree

kennytilton18:07:04

OK, I see, so React Concurrent will "freeze" state so an interrupted rendering, when resumed, will not see any changed state. But it is hard to follow their use case, of a user clicking something to change colors and an in-process rendering picks up the change. But if they mean they will process the mouse-click completely -- I guess they have to in a single-threaded world. OK, makes sense. Every time they fix React, they make it harder to use. Well, I just want their mobile portability via JS, I'll pass on concurrent mode if I see it making a mess of things.

kennytilton18:07:42

We need some clever mobile whiz to distill from React just the layer that hides Android/iOS. 🙂

lilactown19:07:29

it makes sense to me when I think of it in the case of low/high priority of changes

lilactown19:07:46

you can think of it outside of React, too. imagine if you were recalculating the cell graph and it was taking a long time, when the user then clicks a button. maybe the event which triggered the initial recalculation isn't that important, so it would be better to suspend the calculation, recalculate the graph based on the user input, then resume the original calculation where you left off - invalidating any cells which may have changed in the meantime

lilactown19:07:48

it is essential providing an easier mechanism for cooperating on which tasks should hog the thread at any time. component boundaries end up being a convenient way of splitting up the task of calculating the next state of your app, but you could apply the same approach to calculating a cell graph, where each individual cell is the boundary that you check to see if the task should yield to some other task

lilactown19:07:52

however, this breaks when some non-managed mutable variable that your calculation depends on changes between yielding and resuming. thus, the tradeoff

kennytilton19:07:28

The Cell graph does not undergo massive recalculations. Each state change changes one property, which can cascade for a while but only so far. The rendered page may need an overhaul if one of the little changes was selected-page 🙂 , but that is a side-effect outside the Cell graph. It would not be hard to interrupt the external work so a new batch of external work could take over. I just think the FB team has gotten lost in a performance goose chase and they will never get back to the original plan of making web development easy.

lilactown19:07:18

that could be true, but I personally am sympathetic to their cause

lilactown19:07:29

we have some state transitions in our app that take >1s to calculate

lilactown19:07:11

typically it's loading a lot of data into the app, which cascades into a bunch of joins and recalculations of other dependent data

lilactown19:07:34

the UX for this is horrible- the user clicks a button to be taken to a new screen, and the app just freezes

lilactown19:07:35

it would be great to slice that up so the user can still scroll and click things, show some loading indicator if a state transition is taking over a certain threshold

lilactown19:07:04

these states are calculated using re-frame subs, too. the reactive graph approach is only as fast as your slowest cell 😂

lilactown19:07:19

anyhow! excited to try out matrix + react in the future

kennytilton19:07:20

Can you refactor things so less data gets loaded and then massaged in the client? I am thinking asynchronous requests that can be kicked off so we can get back to the user. Also, not loading so much all at once? Yes, I am describing an overhaul... 🙂

Jimmy Miller20:07:24

Just want to add my voice to someone very sympathetic to their cause. If anyone hasn't watched the talks they've given on why, I definitely recommend them. They are really focused on user experience. Even basic things like this demo https://twitter.com/dan_abramov/status/1120986057363939328

Jimmy Miller20:07:47

I do hope for a more accessible global state store being made compatible with concurrent rendering. But I've been a big fan of the changes they've made with react. I've been using it since 2014 and I don't think there is a better time to write applications in it than now.

Jimmy Miller20:07:53

That is why I'm a huge fan of helix. Started a cljs project with vanilla react, found pain points I wanted to fix and compared them to helix. Helix just had better more robust versions of exactly what I was about to write 🙂

kennytilton20:07:19

Thx, @lilactown! I will let you know if I get anywhere, and if I do it will happen much faster because of your work unpacking React. 🙏