reflex
reflex copied to clipboard
Inconsistent Dynamic values
I'm trying to create a Dynamic list that only updates when the length of the list changes. The length is then used to generate a list of indices that is used to create a Dynamic for each list item.
The issue is that when an item is removed from the list, the Dynamic that holds the length of this somehow doesn't change before the elements are retrieved, so it throws an out of bounds error. Furthermore, if I use traceDyn
and modify the index function to ignore invalid indices, I noticed that it iterates over the list twice: once with the old length (which errors) and again for the new length (which doesn't error). So it looks like the Dynamic is somehow getting updated twice, but I have no idea why.
So, my questions are:
- why is the index method being called before the list length dynamic has been updated?
- why does pressing the remove button cause two update events?
testW :: forall t m. MonadWidget t m => m ()
testW = mdo
listD <- foldDyn (\x xs -> if x then 1 : xs else tail xs) [] (leftmost [True <$ addE, False <$ removeE])
addE <- button "add"
removeE <- button "remove"
listLengthD <- holdUniqDyn $ length <$> listD
dyn_ $ ffor listLengthD $ \listLength ->
el "ul" $ sequence_ $ (\i ->
el "li" $ do
listItemD <- holdUniqDyn $ (!! i) <$> listD
dyn_ $ ffor listItemD $ \_ -> text "X"
) <$> [0 .. listLength - 1]
Alternatively, a better approach to this problem would be appreciated. This is a fairly general problem and I'm sure there are better patterns.
UPDATE: I was referred to these solutions, which I'm going to use instead. Regardless, the behavior here seems wrong, or at least not what I expected, so in any case it would be useful to understand it.
-
The problem is that
dyn_
defers it's rendering, so when you remove an item - the innerlistItemD
will update before the outerdyn_
replaces everything with the shorter list. -
listD causes listLengthD and each listItemD to be updated
A better way to handle this is with one of the container functions. For
example
simpleList :: MonadWidget t m => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
On Mon, Sep 17, 2018 at 8:06 AM Joseph Betz [email protected] wrote:
I'm trying to create a Dynamic list that only updates when the length of the list changes. The length is then used to generate a list of indices that is used to create a Dynamic for each list item.
The issue is that when an item is removed from the list, the Dynamic that holds the length of this somehow doesn't change before the elements are retrieved, so it throws an out of bounds error. Furthermore, if I use traceDyn and modify the index function to ignore invalid indices, I noticed that it iterates over the list twice: once with the old length (which errors) and again for the new length (which doesn't error). So it looks like the Dynamic is somehow getting updated twice, but I have no idea why.
So, my questions are:
- why the index method is being called before the list length dynamic has been updated?
- why does pressing the remove button cause two update events?
testW :: forall t m. MonadWidget t m => m () testW = mdo listD <- foldDyn (\x xs -> if x then 1 : xs else tail xs) [] (leftmost [True <$ addE, False <$ removeE]) addE <- button "add" removeE <- button "remove" listLengthD <- holdUniqDyn $ length <$> listD dyn_ $ ffor listLengthD $ \listLength -> el "ul" $ sequence_ $ (\i -> el "li" $ do listItemD <- holdUniqDyn $ (!! i) <$> listD dyn_ $ ffor listItemD $ _ -> text "X" ) <$> [0 .. listLength - 1]
Alternatively, a better approach to this problem would be appreciated.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/reflex-frp/reflex/issues/231, or mute the thread https://github.com/notifications/unsubscribe-auth/ABE45erNugDs5onvCj2HJ0HId0hX4Slaks5ubq9fgaJpZM4WrBJ7 .
Okay, that's starting to clear things up for me. Still have a couple questions though.
- listD causes listLengthD and each listItemD to be updated
Do the two events happen in the same frame?
The problem is that
dyn_
defers it's rendering, so when you remove an item - the innerlistItemD
will update before the outerdyn_
replaces everything with the shorter list.
Do dynamic widgets always update this way? I.e., from inside out.
A better way to handle this is with one of the container functions. For example
simpleList :: MonadWidget t m => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
Thanks. I just refactored to use this and it's much cleaner. :)
I've thought about this a little, and perhaps I was wrong about some of this. I believe I was wrong, dyn_
is deferred but only for it's initialisation so this isn't the problem here.
Every time you update listD you're creating a whole new set of listItemD's. The way dyn_ works is to replace the contents of the inner widget completely every time the event fires.
So if you click add four times you're calling this code 10 separate times. The first time creates 1 listItemD, second time 2 listItemD's, third time 3 listItemD's etc.
listItemD <- holdUniqDyn $ (!! i) <$> listD
When you click remove it does update immediately, however the last set of listItemD's still exists at that point. It's only gone on the next frame. When you click remove you're deleting an element off the list immediately but the listItemD corresponding to the last index is also still exists and causes the indexing error.
Hopefully that is more clear.
When you click remove you're deleting an element off the list immediately but the listItemD corresponding to the last index is also still exists and causes the indexing error.
Okay, so I can't use a Dynamic a
where computing a
might fail, because reflex
will try to compute it even in situations where any dynamics that it has been derived from no longer exist. Is that correct?