-
-
Notifications
You must be signed in to change notification settings - Fork 122
/
View.fs
54 lines (41 loc) · 2.31 KB
/
View.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
namespace Fabulous
module ViewHelpers =
let canReuseView (prevWidget: Widget) (currWidget: Widget) =
let prevKey = prevWidget.Key
if not(prevKey = currWidget.Key) then
false
else if (prevKey = Memo.MemoWidgetKey) then
Memo.canReuseMemoizedWidget prevWidget currWidget
else
true
module View =
/// Avoid recomputing the whole subtree when the key doesn't change
let lazy'<'msg, 'key, 'marker when 'key: equality> (fn: 'key -> WidgetBuilder<'msg, 'marker>) (key: 'key) : WidgetBuilder<'msg, Memo.Memoized<'marker>> =
let memo: Memo.MemoData =
{ KeyData = box key
KeyComparer = fun (prev: obj) (next: obj) -> unbox<'key> prev = unbox<'key> next
CreateWidget = fun k -> fn(unbox<'key> k).Compile()
KeyType = typeof<'key>
MarkerType = typeof<'marker> }
WidgetBuilder<'msg, Memo.Memoized<'marker>>(Memo.MemoWidgetKey, Memo.MemoAttribute.WithValue(memo))
/// Map the widget's message type to the parent's message type to allow for view composition
let inline map (fn: 'oldMsg -> 'newMsg) (x: WidgetBuilder<'oldMsg, 'marker>) : WidgetBuilder<'newMsg, 'marker> =
let replaceWith (oldAttr: ScalarAttribute) =
let fnWithBoxing (msg: obj) =
let oldFn = unbox<obj -> obj> oldAttr.Value
if msg <> null && typeof<'newMsg>.IsAssignableFrom(msg.GetType()) then
box msg
else
oldFn msg |> unbox<'oldMsg> |> fn |> box
{ oldAttr with Value = fnWithBoxing }
let defaultWith () =
let mappedFn (msg: obj) =
if msg <> null && typeof<'newMsg>.IsAssignableFrom(msg.GetType()) then
box msg
else
unbox<'oldMsg> msg |> fn |> box
MapMsg.MapMsg.WithValue(mappedFn)
let builder = x.AddOrReplaceScalar(MapMsg.MapMsg.Key, replaceWith, defaultWith)
WidgetBuilder<'newMsg, 'marker>(builder.Key, builder.Attributes)
/// Combine map and lazy. Map the widget's message type to the parent's message type, and then memoize it
let inline lazyMap (mapFn: 'oldMsg -> 'newMsg) (viewFn: 'key -> WidgetBuilder<'oldMsg, 'marker>) (model: 'key) = lazy' (viewFn >> map mapFn) model