Fork me on GitHub
#re-frame
<
2023-12-15
>
mikerod16:12:10

I was reading this PR comment and the chain of links followable from there. https://github.com/day8/re-frame/issues/680#issuecomment-1676487563 This had me wondering if these same problems don’t come up in the modern React world with hooks and redux? I feel like the re-frame situation should be fairly similar. But I haven’t heard this type of issue in that broader space.

lilactown18:12:43

can you summarize the problems you're wondering about? it's a long post in a long thread

mikerod19:12:27

Well I think the whole problem there matters for discussion. Haha

mikerod19:12:26

But basically dealing with problems of caching reactive state.

mikerod19:12:11

Raising questions such as: should it be cached? When is it cached? What is the life cycle of that caching? Can we be sure to avoid memory leaks from caching not being cleared when no longer needed (basically same as life cycle)?

mikerod19:12:12

The post and links are really good though. But at the end I felt like there were no solutions that sounded satisfying. This made me wonder if you don’t hit the same issues in a “pure react” sort of app. And if not, how is it done differently.

mikerod19:12:17

I think the re-frame flow concept can help. But the post mentions that too. But still thinks there is a need to address the problems outlined for subs. I think I’d agree there.

lilactown20:12:37

yes this comes up in React. a lot of the problems they explain regarding "Suspense for Data Fetching" boils down to caching

lilactown20:12:39

most of React code doesn't think too hard about it because component local state is coupled to the component's lifecycle. So you evict the state when the component is unmounted.

lilactown20:12:44

The problem gets harder when data needs to survive beyond the lifetime of the components that use it, like caching data so that you aren't constantly overfetching.

lilactown20:12:32

IME it's much easier to tackle this on a usecase-by-usecase basis, rather than trying to do something that handles this generally and opaquely. E.g. you don't ever evict the cache when it comes to the UI theme, because the lifetime of that is the lifetime of the app. Same for routing state. But you probably want to set a TTL on some data you fetched so that you don't leak memory.

lilactown20:12:40

Forms are complicated because you only want to maintain the state for the form as long as it's on screen- unless the user hits the back button, then you'd like to reproduce the state of the form 😰

lilactown20:12:56

this is why I think the React ecosystem's current approach to have purpose-built tools (e.g. react-query for data fetching, react-hook-form for forms, emotion for styling/theming, react-router for routing) is better: it can focus on getting the right caching behavior for the actual use case, rather than trying to build something general and opaque that works for every situation (not feasible)

mikerod20:12:12

Ah nice. Thanks for elaborating. I am following what you are saying and I can see how that is basically a similar situation in the pure-react side.

lilactown20:12:10

I think that re-frame currently suffers from the same problems that redux has, and some of it's own unique ones: 1. [redux/re-frame] misses tools to evict the app-db based on some TTL (e.g. data fetching) 2. [redux/re-frame] selectors/subs are tied to the component lifecycle, making it awkward to use outside of a component 3. [redux/re-frame] expects all state to be stored inside of it, making leveraging browser features to do validation/state management for both correctness and performance reasons harder (e.g. forms, routing) 4. [re-frame] async calculation of subs makes it even more awkward to use for things like forms which require coordinating browser state with app-db

👍 1
Kimo09:12:19

Hey guys, thanks for taking a look. It is a pretty intense topic. > But at the end I felt like there were no solutions that sounded satisfying. @U0LK1552A, have you tried out re-frame.subs.alpha? It would be really cool to get your feedback, find out exactly what satisfies or dissatisfies. There's a https://github.com/day8/re-frame/blob/d001d765869fd2c1c08b4e31b381d1c2d2a475f5/examples/todomvc/src/todomvc/views.cljs#L95, and you can see the full range of behavior in https://github.com/day8/re-frame/blob/d001d765869fd2c1c08b4e31b381d1c2d2a475f5/test/re_frame/subs/alpha_test.cljs.

👍 1
Kimo10:12:18

I think flows are an elegant solution, but they do require you to re-think your design a bit. It's not always obvious how you'd refactor a subscription into a flow, for example. The new subscriptions are a seamless extension of current subscriptions, so I think the dev experience can be pretty nice.

Kimo10:12:39

& if anyone wants help testing out the subs alpha, feel free to ask around the channel or DM me.

p-himik10:12:54

@U4YGF4NGM Points 1 and 3 go away if you consider re-frame to be a low-level library that you're supposed to build other things with. I'm definitely not as well versed in the React ecosystem as you are, but just as an example I find kee-frame to be a nice approach to routing. 2 is a valid point, and that's exactly what flows are for. Can't comment on 4 at all because the last time I checked browser API for form validation was years ago and it was so incredibly limiting and not compatible between browsers that I decided to just stick to JS. Maybe by "browser state" you mean something else, in that case could you please specify?

mikerod15:12:47

Thanks for all the input. It’s good to hear more insights on this topic. @U02J388JDEG I indeed want to try out the sub alpha stuff and flows in some complex apps I have and see how well it works out. Hopefully I can do a bit of that within the next week or so.

DrLjótsson08:12:21

@U4YGF4NGM and @U2FRKM4TW, I'm following your discussion with great interest! I'm developing a re-frame app and have not yet started to implement more complex forms. But they will come. So it would be awesome to hear more about your experiences about the difficulties and solutions regarding forms in re-frame. @U4YGF4NGM, what are the specific problems that one encounters when working with forms in re-frame? And how do they relate to controlled and uncontrolled components? @U2FRKM4TW, how have you solved problems that you have encountered and why do you prefer to have all inputs controlled - how have you solved the issues (?) that come with controlled inputs?

p-himik09:12:00

> how have you solved problems that you have encountered Hard to come up with something specific apart from "use dispatch-sync if necessary". > why do you prefer to have all inputs controlled Because I prefer to have a full control over an input. Input fields are a reflection of the data in app-db - not of user input. User input affects app-db, and only that affects inputs. Without it, the app can't change the data in an input field in a reasonable way. There can be no global undo/redo, no global inspection, no comprehensive app state reporting where I can just download a ZIP and have the same exact state that a user had. Of course, there are probably workarounds for that in the React ecosystem, but I prefer a simple direct approach. > how have you solved the issues (?) that come with controlled inputs? Either I haven't encountered them I haven't perceived them as issues.

1
DrLjótsson09:12:00

Thanks! I've encountered some problems with ctrl-z, i.e., the user wanting to undo input, when working with controlled inputs. Do you recognize this?

p-himik09:12:26

No recollection. Have you tried dispatch-sync? Apart from that, there's a trick with a local ratom that re-com commonly uses and Reagent suggests when working with some JS components that don't behave nicely.

DrLjótsson09:12:10

Do you generally use re-com or wrapped inputs à la re-com? IIRC, re-com only saves data to app-db when the component is blurred - do you find this to be the best way? I realize these are very general questions but I'm grateful for all experiences from more seasoned re-frame developers.

p-himik09:12:43

I don't use raw HTML/Hiccup for inputs. In data-heavy projects I use re-com, in others I use MUI. > re-com only saves data to app-db when the component is blurred It's configurable with :change-on-blur?, I have it set to false because it suits the nature of my projects more.

DrLjótsson14:12:02

Yes of course, it's configurable. I'll keep asking and please only respond if you have time - where do you put data validation? In the component, as part of the event that receives data from the form, or as a derived sub?

p-himik15:12:52

I always treat "this value is invalid" as a part of the app's state, so it goes into app-db. Since the validation state is useful not just to views but also to events, it can't be a sub, it has to be a part of app-db. Where exactly it resides and how it gets there depends on what kind of validation data it is. If it's for a transient form that a user opens, fills in, and submits, then it's a part of the form's data. The simplest thing to do here is to use the built-in on-changes interceptor on the events that change the form's data and validate the data there, maybe with a debounced event so as not to run some particularly expensive validation on every keystroke. Sometimes on-changes is not enough, but it's trivial to write your own interceptor. If, on the other hand, the validation is for some kind of common state that can be used in a generic way by many parts of the app, then a global interceptor that runs some generalized validations and writes the results into a specific but also generalized location will do.

🙏 1
p-himik15:12:19

That approach is also incredibly flexible. It becomes trivial to have validations that can span the whole state of the app, without having to pass just the right data into each and every component that might need to validate something. Also trivial to have async validations, regardless of their nature.

🙏 1
DrLjótsson15:12:37

@U2FRKM4TW , what do you mean by async validation? Server-side? This is all incredibly helpful!

DrLjótsson15:12:31

@U4YGF4NGM , if you have the energy and time to expand on the problems with using re-frame and forms, that would also be appreciated. I think that could give good insights into pitfalls or routes one should avoid.

1
DrLjótsson15:12:19

@U2FRKM4TW , btw, do you see flows as a way to decrease complexity around form validation?

lilactown17:12:40

1. Form state usually needs to be tied to the lifetime of the form being displayed on the screen, which means that you need to initialize it on mount and evict it when you navigate away (except when you hit back). 2. Form inputs being uncontrolled is good for performance, especially when interoping with non-reagent components like react-select, MUI, etc. that don't have reagent's "input hack" in place. ultimately these things have answers that boil down to "add more code" and "do the right thing." I personally have grown fond of react-hook-form, which provides an abstraction for handling these problems using the right tools (a combination of component local state and native DOM state plus APIs for integrating with other state management solutions like react-query, re-frame, etc.)

🙏 1
Kimo17:12:51

so maybe a clojure version of react-hook-form would be useful. especially if it supported both reagent and uix/helix

👍 1
p-himik17:12:46

> APIs for integrating with other state management solutions like react-query, re-frame, etc.) Oh, so it seems that all the good things can be used together and it's not really a "X vs Y" comparison but rather "(X w/ Y) vs (X w/o Y)".

👍 1