简体   繁体   English

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

[英]How can I efficiently branch on the value inside a Reflex.Dynamic?

Let's say I have some application state, maintained on some backend system. 假设我有一些应用程序状态,在某些后端系统上维护。 It looks like this 看起来像这样

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 }

I also have a function to get the latest state from the backend system 我还有一个从后端系统获取最新状态的功能

getLatestState :: IO MyState

I'm pretty sure I can figure out how to package that up into a Dynamic by repeatedly querying the backend, so that I have 我很确定我可以通过反复查询后端来弄清楚如何将其打包成Dynamic,这样我就拥有了

dynMyState :: MonadWidget t m => Dynamic t MyState

I want to render this to html. 我想将此渲染为html。 I want each part of the datastructure to render to a div. 我希望数据结构的每个部分都呈现为div。 However, things that don't exist shouldn't be rendered at all - so, when ms2_maybe_char is Nothing , there should be no div for it, and when MyState is a State1 , there should be no div for the State2 . 但是,根本不应该呈现不存在的东西 - 因此,当ms2_maybe_charNothing ,它应该没有div,而当MyStateState1State2应该没有div。

a couple examples for clarity: 一些清晰的例子:

State1 (MyState1 "foo" 3)

becomes

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

and

State2 (MyState2 False Nothing)

becomes

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

Ideally, each part of the DOM should only be modified if necessary - so if ms2_maybe_char changes from Nothing to Just 'a' , then a new div needs to be created. 理想情况下,只有在必要时才应修改DOM的每个部分 - 因此,如果ms2_maybe_charNothing更改为ms2_maybe_char Just 'a' ,则需要创建新的div。 Or if ms1_text changes from "foo" to "bar" , then we need to change that string in the DOM. 或者如果ms1_text"foo"变为"bar" ,那么我们需要在DOM中更改该字符串。 However, changing ms1_text should never cause the sibling or parent nodes to be redrawn. 但是,更改ms1_text不应该导致重绘兄弟节点或父节点。

How should I structure my code? 我应该如何构建我的代码? Is this even possible, given the getLatestState api as a building block? 考虑到getLatestState api作为构建块,这是否可行? Am I entirely missing the point of Reflex by trying to build off of a single Dynamic value, and I need to rethink my approach? 通过尝试构建单个Dynamic值,我完全忽略了Reflex的观点,我需要重新思考一下我的方法吗?

In particular, the very first stumbling block is that I can't easily inspect the Dynamic to know if it contains a State1 or a State2. 特别是,第一个绊脚石是我无法轻易检查Dynamic以了解它是否包含State1或State2。 I could potentially use dyn or widgetHold here, and fmap a function over dynMyState which can treat the state as a simple value and generate a m () action that draws the whole thing. 我可能会使用dynwidgetHold这里,和fmap在功能dynMyState它可以把国家作为一个简单的值,并生成一个m ()吸引了整个事情的行动。 But, then I lose all sharing - the entire UI will be redrawn from scratch on every single state change. 但是,然后我失去了所有共享 - 整个UI将在每次状态更改时从头开始重绘。

Note: this is a more detailed followup question to How can I branch on the value inside a Reflex Dynamic? 注意:这是一个更详细的后续问题如何分支Reflex Dynamic中的值? . What's different/clearer about this question is the additional desire to not lose efficient updates of everything inside the inspected value. 这个问题的不同/更清楚的是,不要失去对检查价值内部所有内容的有效更新的额外愿望。 Thanks to everyone who helped on that question as well! 感谢所有帮助解决这个问题的人!

The answer depends on exactly what your goals and requirements are. 答案取决于您的目标和要求。 If you want the best possible dom sharing, derived from two separate functions renderState1 and renderState2 , I think that calls for virtual-dom . 如果你想要最好的dom共享,从两个独立的函数renderState1renderState2派生,我认为调用了virtual-dom

But it actually sounds like you want to have some precise control over what gets added to the DOM when. 但实际上,您似乎希望对何时添加到DOM中的内容进行精确控制。

Something simple you can do, if you have modified versions of renderState1 and renderState2 in hand that each take a Maybe State1 or Maybe State2 argument is to build a pair of these dynamic maybes and use css attributes to hide one or the other: 你可以做一些简单的事情,如果你已经修改了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

If you derive Prisms then a lot of the awkwardness can be gotten rid of. 如果你衍生出棱镜那么可以摆脱很多尴尬。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM