如何有效地分支Reflex.Dynamic中的值?

时间:2016-11-08 22:05:10

标签: haskell reflex

假设我有一些应用程序状态,在某些后端系统上维护。看起来像这样

data MyState = State1 MyState1 | State2 MyState2

data MyState1 = MyState1 { ms1_text :: Text, ms1_int :: Int }
data MyState2 = MyState2 { ms2_bool :: Bool, ms2_maybe_char :: Maybe Char }

我还有一个从后端系统获取最新状态的功能

getLatestState :: IO MyState

我很确定我可以通过反复查询后端来弄清楚如何将其打包成Dynamic,以便我有

dynMyState :: MonadWidget t m => Dynamic t MyState

我想将此渲染为html。我希望数据结构的每个部分都呈现为div。但是,根本不应该呈现不存在的东西 - 因此,当ms2_maybe_charNothing时,它应该没有div,而当MyStateState1时1}},State2应该没有div。

为清晰起见,有几个例子:

State1 (MyState1 "foo" 3)

变为

<div class=MyState1>
    <div>foo</div>
    <div>3</div>
</div>

State2 (MyState2 False Nothing)

变为

<div class=MyState2>
    <div>False</div>
</div>

理想情况下,DOM的每个部分都应该在必要时进行修改 - 因此如果ms2_maybe_charNothing更改为Just 'a',则需要创建新的div。或者,如果ms1_text"foo"更改为"bar",那么我们需要在DOM中更改该字符串。但是,更改ms1_text不应该导致重绘兄弟节点或父节点。

我应该如何构建代码?考虑到getLatestState api作为构建块,这是否可能?通过尝试构建单个Dynamic值,我完全忽略了Reflex的观点,我需要重新考虑我的方法吗?

特别是,第一个绊脚石是我无法轻易检查Dynamic以了解它是否包含State1或State2。我可以在这里使用dynwidgetHold,并fmap使用dynMyState以上的函数,可以将状态视为简单值并生成m ()操作画出了整个事物。但是,然后我失去了所有共享 - 整个UI将在每次状态更改时从头开始重绘。

注意:这是How can I branch on the value inside a Reflex Dynamic?的更详细的后续问题。这个问题的不同/更清楚的是,不要失去对检查价值内部所有内容的有效更新的额外愿望。感谢所有帮助解决这个问题的人!

1 个答案:

答案 0 :(得分:0)

答案取决于您的目标和要求。如果您希望从两个单独的函数renderState1renderState2中获得最佳的dom共享,我认为这需要virtual-dom

但实际上听起来你想对在什么时候添加到DOM的内容有一些精确的控制。

你可以做的很简单,如果你手头有renderState1renderState2的修改版本,每个版本都需要Maybe State1Maybe State2参数构建一对这些动态maybes并使用css属性来隐藏其中一个:

let mState1 = (\c -> case c of
                  s@(State1 _ _) -> Just s
                  _              -> Nothing
              ) <$> dynMyState
    mState2 = (\c -> case c of
                  s@(State2 _ _ _) -> Just s
                  _                -> Nothing
    nothingHider a m =
       let atr = bool mempty ("style" =: "displayNone") . isJust <$> a
       in  elDynAttr "div" atr (m a)

nothingHider mState1 renderMaybeState1
nothingHider mState2 renderMaybeState2

如果你derive Prisms那么可以摆脱很多尴尬。