-
Notifications
You must be signed in to change notification settings - Fork 17
Unexpected behaviour of andd
#28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
From the code snippet and your description, it does look like a bug in the
implementation of ‘andd’. I’m currently travelling so I will take a look at
this later today.
…On Sat, 24 Aug 2019 at 16:57, Tim Steenvoorden ***@***.***> wrote:
I'm experimenting with Widget composability and decided to try out the
form example given in the documentation
<https://github.com/ajnsit/concur-documentation/blob/master/README.md#replicating-elm-architecture>.
I created a gist
<https://gist.github.com/timjs/eb859d552e7113281f402c7d92e45449>
containing all the code for reference.
Creating a multi form step-by-step widget using traverse works as
expected. However, composing them side-by-side using andd results in
surprising behaviour (at least for me), when following these steps:
- Start a side-by-side composition of the form widget using andd. This
results in three forms, laid out on top of each other.
- Change every input field of the form into something else, for
example "1", "2", "3".
- Submit one of the forms by hitting the "Submit" button.
- Now there are two forms on the screen, as expected, but the state of
these forms *returned to their initial value*!
I expected each individual form to keep their changed state. Is this
expected behaviour or a bug? Should I write other code to make this work as
I expect?
|
That's fine! Good travels. |
On second thoughts, this does not seem like a bug. When you set a “value” attribute on an input, it’s value will be reset on any state change. React provides a “defaultValue” attribute for this very problem. Can you try changing the attributes to “defaultValue”? |
I'll try it out and let you know! |
Works better, but still not as I expect:
|
With defaultValue attribute, you are letting React manage the value of the
input. So you need to provide React with some way to distinguish between
the form instances. Adding a “key” attribute to the form would help (it
also speeds up rendering).
…--
Sent from my hyper-communicator
|
So I’ll need to generate an unique key/ID for every form in the list? I hoped I wouldn’t need to do that with Concur, kind of the big selling point when comparing it with TEA. Like in the tree example where you do not need to index of the subtrees. Could you help me recreating this example using widgets only instead of signals? I've an implementation in this gist, but probably made some kind of mistake as I did in the form example, as state does not get saved correctly. |
Should I add the key to every element? Adding it only to the |
No you only need it at the top level. To get the same behaviour with
checkboxes you need to use ‘defaultChecked’ instead of ‘checked’.
You can read up more about default values in React here -
https://reactjs.org/docs/uncontrolled-components.html#default-values
…On Sun, 25 Aug 2019 at 15:08, Tim Steenvoorden ***@***.***> wrote:
Should I add the key to every element? Adding it only to the div
<https://gist.github.com/timjs/eb859d552e7113281f402c7d92e45449#file-form-purs-L33>
results in saving the values of the input fields, but all checkbox are
cleared on a submit.
|
Obviously! Thanks :-) |
Well yeah. It’s not ideal. There is some discussion about Concur keeping
track of the components that change itself without relying on the backend
(react in this case).
However, to clarify one thing - this is only a problem because you want the
form elements’ state to persist without handling it in Concur. There is
some more discussion on better state handling in Concur which might solve
this problem.
…On Sun, 25 Aug 2019 at 14:58, Tim Steenvoorden ***@***.***> wrote:
So I’ll need to generate an unique key/ID for every form in the list? I
hoped I wouldn’t need to do that with Concur, kind of the big selling point
when comparing it with TEA. Like in the tree example
<https://github.com/ajnsit/purescript-concur/blob/master/examples/src/Test/EditHeadings.purs>
where you do not need to index of the subtrees.
Could you help me recreating this example using widgets only instead of
signals? I've an implementation in this gist
<https://gist.github.com/timjs/fd48c0e9f675134b7a09a8d6424e0cb7>, but
probably made some kind of mistake as I did in the form example, as state
does not get saved correctly.
|
So should I use a similar to trick with keys with the tree widget example? Or am I doing something else wrong? PS Thanks for responding so quickly to my questions! No need to respond immediately if you're trying to enjoy your Sunday however ;-) |
Well you can't just |
@timjs I'm going to close this issue. Please feel free to reopen in case you have more questions or comments! |
Thanks Anupam for helping me out. This is an intuitive solution. So, if |
I think the question of whether the "state" is saved "internally" or not,
is a red herring.
As a rule, there is no state inside Concur, apart from that captured by the
time sequencing monad (the currently executing step in the sequence), and
the (pure and immutable) arguments passed around to functions. If the state
you are looking to save is not captured in an argument, and is not
determined by the position in the monadic sequence, then Concur does not
deal with it.
Given that, it should be possible to understand the semantics of any widget
or widget combinator in Concur by just its type signature.
andd :: forall a. Array (Widget HTML a) -> Widget HTML (Array a)
Here `andd` takes an array of widgets, and returns a single widget. Once
you have combined the widgets with `andd`, there is no internal state of
any internal widget that's visible outside. Only when the entire `andd`
returns the `result` are we able to examine the result and possibly save it
ourselves. Monadic sequencing cannot see inside `andd` since at this level
it's just an opaque single widget.
Now we know that `andd` does not return a value until all of its
constituents return themselves. So if one or more of the constituents are
widgets with intermediate steps, there is no way to get access to those
intermediate results from outside `andd`.
This might seem limiting, but it follows directly from the type signature,
and is expected. Mostly `andd` was created as a dual to `orr`, and to
provide a nice contrast with `traverse` in the documentation :) However, as
`displayList` demonstrates, it's easy to create your own generic
combinators with any semantics you want. Infact you are encouraged to do
so, as ease of composability is the USP of Concur.
Note that all is not lost, and `andd` could still be useful in the
following circumstances when -
1. You have simple widgets that either don't have intermediate steps, or
are fine with losing intermediate state.
2. No ancestors of the `andd` have any dynamic actions attached. Such as a
bunch of forms at the top level that all need to be filled.
3. Even if any ancestor of the `andd` has any dynamic actions attached,
those are expected to reset the intermediate state of the children. Such as
when you have page switching controls at the top, and it's understood that
switching pages loses the state of the forms.
… Thanks Anupam for helping me out. This is an intuitive solution.
So, if andd does not save the state of the widgets internally, what is
the intended use case of it?
|
I totally agree with this way of thinking. It would be odd to have a way to "peak into" the current state of a widget in the array. I expect that, after feeding a list of widgets to However, the mistake I made was that every kind of event which would change the view of, for example, an input box would be remembered. But that is not true for basic Dom elements, as you explained. I see now that inputbox :: String -> Widget HTML String
inputbox value = do
result <-
Html.input
[ Html.autoFocus true
, Html._type "text"
, Html.value value
, Html.placeholder value
, Just <$> Html.unsafeTargetValue <$> Html.onInput
, Nothing <$ Html.onKeyEnter
]
case result of
Just value' -> inputbox value'
Nothing -> pure value |
Glad to hear it! |
I'm experimenting with Widget composability and decided to try out the form example given in the documentation. I created a gist containing all the code for reference.
Creating a multi form step-by-step widget using
traverse
works as expected. However, composing them side-by-side usingandd
results in surprising behaviour (at least for me), when following these steps:andd
. This results in three forms, laid out on top of each other.I expected each individual form to keep their changed state. Is this expected behaviour or a bug? Should I write other code to make this work as I expect?
Some background: I'm trying to rewrite the edit headings example using widgets instead of signals. Here I ran into the same problem. Probably going to switch to the Haskell version of Concur, because I need some Haskell libs on the backend. I did not try the form example with Haskell yet.
The text was updated successfully, but these errors were encountered: