假设我有一些应用程序状态,在某些后端系统上维护。看起来像这样
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_char
为Nothing
时,它应该没有div,而当MyState
是State1
时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_char
从Nothing
更改为Just 'a'
,则需要创建新的div。或者,如果ms1_text
从"foo"
更改为"bar"
,那么我们需要在DOM中更改该字符串。但是,更改ms1_text
不应该导致重绘兄弟节点或父节点。
我应该如何构建代码?考虑到getLatestState
api作为构建块,这是否可能?通过尝试构建单个Dynamic
值,我完全忽略了Reflex的观点,我需要重新考虑我的方法吗?
特别是,第一个绊脚石是我无法轻易检查Dynamic以了解它是否包含State1或State2。我可以在这里使用dyn
或widgetHold
,并fmap
使用dynMyState
以上的函数,可以将状态视为简单值并生成m ()
操作画出了整个事物。但是,然后我失去了所有共享 - 整个UI将在每次状态更改时从头开始重绘。
注意:这是How can I branch on the value inside a Reflex Dynamic?的更详细的后续问题。这个问题的不同/更清楚的是,不要失去对检查价值内部所有内容的有效更新的额外愿望。感谢所有帮助解决这个问题的人!
答案 0 :(得分:0)
答案取决于您的目标和要求。如果您希望从两个单独的函数renderState1
和renderState2
中获得最佳的dom共享,我认为这需要virtual-dom
。
但实际上听起来你想对在什么时候添加到DOM的内容有一些精确的控制。
你可以做的很简单,如果你手头有renderState1
和renderState2
的修改版本,每个版本都需要Maybe State1
或Maybe 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那么可以摆脱很多尴尬。