Skip to content

Commit

Permalink
Merge pull request #1074 from fabulous-dev/component-binding
Browse files Browse the repository at this point in the history
Add support for Binding in Components
  • Loading branch information
TimLariviere committed Mar 25, 2024
2 parents 924549c + 177b6e4 commit 7cb2461
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 4 deletions.
61 changes: 61 additions & 0 deletions src/Fabulous/Binding.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
namespace Fabulous

open System
open System.ComponentModel
open System.Runtime.CompilerServices

type BindingRequest<'T> = delegate of unit -> StateValue<'T>

[<Struct>]
type BindingValue<'T> =
[<EditorBrowsable(EditorBrowsableState.Never)>]
val public SourceContext: ComponentContext

[<EditorBrowsable(EditorBrowsableState.Never)>]
val public SourceKey: int

new(stateValue: StateValue<'T>) =
{ SourceContext = stateValue.Context
SourceKey = stateValue.Key }

member this.Current = this.SourceContext.TryGetValue<'T>(this.SourceKey).Value

member inline this.Set(value: 'T) =
this.SourceContext.SetValue(this.SourceKey, value)

[<AutoOpen>]
module BindingBuilders =
type Context with

static member inline Binding(value: StateValue<'T>) = BindingRequest<'T>(fun () -> value)

[<Extension>]
type BindingExtensions =
[<Extension>]
static member inline Bind
(
_: ComponentBuilder<'parentMsg>,
[<InlineIfLambda>] fn: BindingRequest<'T>,
[<InlineIfLambda>] continuation: BindingValue<'T> -> ComponentBodyBuilder<'marker>
) =
ComponentBodyBuilder<'marker>(fun bindings ctx ->
let key = int bindings
let stateValue = fn.Invoke()

// Dispose previous subscription
match ctx.TryGetValue<IDisposable>(key) with
| ValueNone -> ()
| ValueSome d -> d.Dispose()

// Subscribe to source context changes
let sourceKey = stateValue.Key

let sub =
stateValue.Context.RenderNeeded.Subscribe(fun k ->
if k = sourceKey then
ctx.NeedsRender(key))

ctx.SetValueInternal(key, sub)

let bindingValue = BindingValue<'T>(stateValue)
(continuation bindingValue).Invoke(bindings, ctx))
2 changes: 1 addition & 1 deletion src/Fabulous/Component.fs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ type Component(treeContext: ViewTreeContext, body: ComponentBody, context: Compo
interface IDisposable with
member this.Dispose() = this.Dispose()

member this.Render() =
member this.Render(_) =
treeContext.SyncAction(this.RenderInternal)

module Component =
Expand Down
10 changes: 7 additions & 3 deletions src/Fabulous/ComponentContext.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ type ComponentContext(initialSize: int) =
let mutable values = Array.zeroCreate initialSize
let disposables = System.Collections.Generic.List<IDisposable>()

let renderNeeded = Event<unit>()
let renderNeeded = Event<int>()

// We assume that most components will have few values, so initialize it with a small array
new() = new ComponentContext(3)

member this.Id = id

member this.RenderNeeded = renderNeeded.Publish
member this.NeedsRender() = renderNeeded.Trigger()
member this.NeedsRender(key: int) = renderNeeded.Trigger(key)

member private this.ResizeIfNeeded(count: int) =
// If the array is already big enough, we don't need to do anything
Expand All @@ -66,7 +66,7 @@ type ComponentContext(initialSize: int) =

member this.SetValue(key: int, value: 'T) =
this.SetValueInternal(key, value)
this.NeedsRender()
this.NeedsRender(key)

member this.LinkDisposable(disposable: IDisposable) = disposables.Add(disposable)

Expand All @@ -76,6 +76,10 @@ type ComponentContext(initialSize: int) =

disposables.Clear()

for value in values do
if value :? IDisposable then
(value :?> IDisposable).Dispose()

values <- Array.empty

interface IDisposable with
Expand Down
1 change: 1 addition & 0 deletions src/Fabulous/Fabulous.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<Compile Include="ComponentContext.fs" />
<Compile Include="Component.fs" />
<Compile Include="State.fs" />
<Compile Include="Binding.fs" />
<Compile Include="MvuComponent.fs" />
<Compile Include="Memo.fs" />
<Compile Include="View.fs" />
Expand Down

0 comments on commit 7cb2461

Please sign in to comment.