如何在Reflex.Dynamic中有效地分支值?
比方说,我有一些应用程序状态,在一些后端系统上维护。它看起来像这样如何在Reflex.Dynamic中有效地分支值?
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
我敢肯定我能弄清楚如何包成一个动态的获得最新的状态反复查询后端,以便我有
dynMyState :: MonadWidget t m => Dynamic t MyState
我想将其呈现为html。我希望数据结构的每个部分都呈现给div。但是,不存在的东西根本不应呈现 - 因此,当ms2_maybe_char
为Nothing
时,应该没有div,而当MyState
是State1
时,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是一个构建块,这甚至有可能吗?我是否完全错过了Reflex的重点,试图建立一个单一的Dynamic
价值,我需要重新思考我的方法?
特别是,第一个绊脚石是我不能轻易检查Dynamic来知道它是否包含State1或State2。我可以在这里使用dyn
或widgetHold
,而fmap
可以使用dynMyState
以上的函数,它可以将状态视为一个简单值并生成描绘整个事物的m()
动作。但是,然后我失去了所有的共享 - 整个UI将在每次状态改变时从头开始重新绘制。
注意:这是一个更详细的后续问题How can I branch on the value inside a Reflex Dynamic?。关于这个问题有什么不同/更清晰的是,额外的愿望是不会失去被检查价值内所有东西的有效更新。感谢所有帮助过这个问题的人!
答案取决于你的目标和要求。如果你想要最好的dom共享,从两个单独的函数renderState1
和renderState2
派生,我认为需要virtual-dom
。
但它实际上听起来像你想有一些精确的控制什么时候被添加到DOM。
事情简单,你可以做,如果你修改了在手的renderState1
和renderState2
版本,每个采取Maybe State1
或Maybe State2
论点是建立一个对这些动态maybes,并使用CSS属性隐藏一个或另一个:
let mState1 = (\c -> case c of
[email protected](State1 _ _) -> Just s
_ -> Nothing
) <$> dynMyState
mState2 = (\c -> case c of
[email protected](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那么很多尴尬可以摆脱。
我的答案是:要么小部件足够小,性能无关紧要,要么需要实现dyn/widgetHold等价物,它们使用'virtual-dom'进行高性能渲染。但是我希望有人对此有更多的了解,所以我只是留下评论。 – Bartosz