我如何有效地分支Reflex.Dynamic内的值? [英] How can I efficiently branch on the value inside a Reflex.Dynamic?

查看:103
本文介绍了我如何有效地分支Reflex.Dynamic内的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  data MyState = State1 MyState1 | State2 MyState2 
$ b 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 tm =>动态t MyState 

我想将其渲染为html。我希望数据结构的每个部分都呈现给div。但是,不存在的东西不应该被渲染 - 所以,当 ms2_maybe_char Nothing 时,那里应该没有div,当 MyState State1 时,<$ c应该没有div


$ p
$ b pre $ p
$ b pre $ p $ > State1(MyState1foo3)

变成

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

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



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



注意:这是一个更详细的后续问题我如何分支值反射动态?。关于这个问题有什么不同/更清晰的是,额外的愿望是不会失去被检查价值内所有东西的有效更新。感谢所有帮助过这个问题的人!

解决方案

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



但实际上,您似乎想要对DOM中添加的内容进行精确控制如果您修改了 renderState1 的版本,那么您可以执行一些简单的操作。

renderState2 手中每一个都带有 Maybe State1 也许State2 参数是构建一个这些动态maybes对,并使用css属性来隐藏其中一个或另一个:

  let mState1 =(\c  - >情况c的
s @(State1 _ _) - >只是s
- - >无
)< $> dynMyState
mState2 =(\c - > case c of
s @(State2 _ _ _) - >只是s
- - > Nothing
nothingHider am =
让atr = bool mempty(style=:displayNone)。isJust< $> a在elDynAttr中的
divatr(ma)

nothingHider mState1 renderMaybeState1
nothingHandler mState2 renderMaybeState2

如果派生Prisms ,那么很多尴尬可以被消除。

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

dynMyState :: MonadWidget t m => Dynamic t MyState

I want to render this to html. I want each part of the datastructure to render to a 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.

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. Or if ms1_text changes from "foo" to "bar", then we need to change that string in the DOM. However, changing ms1_text should never cause the sibling or parent nodes to be redrawn.

How should I structure my code? Is this even possible, given the getLatestState api as a building block? 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?

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. 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. But, then I lose all sharing - the entire UI will be redrawn from scratch on every single state change.

Note: this is a more detailed followup question to How can I branch on the value inside a 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.

But it actually sounds like you want to have some precise control over what gets added to the DOM when.

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:

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.

这篇关于我如何有效地分支Reflex.Dynamic内的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆