liqula / react-hs

A GHCJS binding to React based on the Flux design. The flux design pushes state and complicated logic out of the view, allowing the rendering functions and event handlers to be pure Haskell functions.
32 stars 10 forks source link

Under what circumstances will DOM state / local view state be reset #41

Closed tysonzero closed 7 years ago

tysonzero commented 7 years ago

Particularly since I am very interested in making local state more powerful and decoupling from Flux, I am curious under what circumstances the view/dom state will be reset. Particularly considering that it needs to be done in a referentially transparent way without violating Haskell's semantics.

Are there any denotational semantics for this resetting? Also I know it uses unsafePerformIO internally, is this use actually safe. To me something it seems like it probably doesn't. Something like:

let foo = view_ myView "" in div_ $ foo *> foo

div_ $ view_ myView "" *> view_ myView ""

Seems to me to potentially violate referential transparency. The second should clearly have two separate internal states, but that would also require the first to do the same, so does it?

I will test this sometime but was curious if this was documented somewhere, some denotational semantics of sorts would be great.

divipp commented 7 years ago

Hi, I am also interested in this subject, also giving priority to denotational semantics.

I am going to investigate this in the next few days. I plan to post here what I have found.

fisx commented 7 years ago

the short answer: currently, the state is kept until the props change and the component is re-rendered. at that moment, the state is wiped.

a better semantics would be that the stateful component takes a function props -> state -> state that creates a new initial state on props change from the new props value and the old state.

for now, i think the cleanest solution is to create a new store just for the stateful component, and use that to carry the state value over between re-renderings. this needs some more hacking if you plan to render the component more than once, but if you keep an identifier in the props you could use that to access the part of the store matching a specific rendering. (if anybody could translate this into react-speak i'd be grateful :-)

note that i'm changing my mind again, and getting more optimistic about having multiple store values flying around flux-style. but we haven't investigated that yet.

fisx commented 7 years ago

(and i agree with your bad feeling about referential transparency. haven't digged into it, but see #4.)

divipp commented 7 years ago

33 is related

fisx commented 7 years ago

right. in fact, it's a duplicate. thanks for spotting it. i'll close this here then.

tysonzero commented 7 years ago

for now, i think the cleanest solution is to create a new store just for the stateful component, and use that to carry the state value over between re-renderings. this needs some more hacking if you plan to render the component more than once, but if you keep an identifier in the props you could use that to access the part of the store matching a specific rendering. (if anybody could translate this into react-speak i'd be grateful :-)

I mean half the reason I am looking into using stateful components over stores is for the exact purpose of having multiple independent instances of them. I would very very strongly urge against requiring some sort of shared map and having each component keep track of its own key.

Currently in boring old react.js it is very easy to have stateful views that you duplicate and interact with very easily. I was really hoping for no usability decrease with the switch to Haskell, rather the exact opposite with all the powerful language features Haskell has.

Honestly I would much prefer having re-rendering the parent reset the child class states than not being able to have multiple children without a lot of extra effort. Particularly since you can always float up state that you want to persistent through certain re-renders, or you can even put them in Stores explicitly.

So to me such a change would be a big downgrade, as it does not give you any new functionality that wasn't already easy to obtain, and severely negatively affects certain existing functionality.

tysonzero commented 7 years ago

Oh and on a side note if the state is wiped on re-render then it seems very doable to make this referentially transparent if it isn't already. The more difficult situations arise when trying to do any re-render state persistence, at which point we do have to think a lot more about referential transparency and being real careful about any dirty "create an IORef of sorts with unsafePerformIO and just keep referring back to it" as it will almost certainly break referential transparency.

I am probably going to think a ton about this general issue and try and come up with something, as it seems very key to making this library modular and allowing for reusable and decoupled components. I will try and submit some sort of cohesive design about the type signatures and denotational semantics I think will work well, but it actually does seem like a very hard task to get just right.

fisx commented 7 years ago

Several points:

your designs would be very welcome. meanwhile, we're too busy working on an product based on this, but we are also thinking about this a lot. let's see what we can come up with.