3

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

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_charis时Nothing,它不应该有 div,而当MyStateis a时State1,不应该有 div State2

为了清楚起见,举几个例子:

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_charNothingto更改Just 'a',则需要创建一个新的 div。或者如果ms1_text"foo"变为"bar",那么我们需要在 DOM 中更改该字符串。但是,更改ms1_text不应导致重绘兄弟节点或父节点。

我应该如何构建我的代码?getLatestState考虑到api 作为构建块,这甚至可能吗?我是否完全错过了 Reflex 的要点,试图建立一个单一的Dynamic价值,我需要重新考虑我的方法?

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

注意:这是一个更详细的后续问题,我如何在 Reflex Dynamic 内的值上进行分支?. 这个问题的不同/更清楚的是,不丢失检查值内所有内容的有效更新的额外愿望。感谢所有在这个问题上提供帮助的人!

4

1 回答 1

0

答案取决于您的目标和要求。如果你想要最好的 dom 共享,从两个独立的函数renderState1和派生renderState2,我认为需要virtual-dom.

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

一些简单的事情你可以做,如果你有修改版本renderState1renderState2手头,每个接受一个Maybe State1orMaybe State2参数是构建一对这些动态可能并使用 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

如果您导出 Prisms,那么可以摆脱很多尴尬。

于 2017-03-06T02:07:49.913 回答