Avalonia.FuncUI
Avalonia.FuncUI copied to clipboard
[Elmish] Button.onClick handlers not getting updated by view
A button, once created, will update its content in response to a new view, but will not update its onClick. This leads to buttons that say they'll do one thing actually doing another.
Repro: 1.) Clone https://github.com/MaxWilson/AvaloniaRepro 2.) Dotnet run 3.) In the app, click on "Add Ryan" and then "Say something Ryan"
Expected: it should say "Ryan: Blahblahblah". Actual: "Bob: Blahblahblah".
type Model = {
people: string list
chatLog: string list
}
type Msg =
| Speak of person:string * msg: string
| NewPerson of name:string
let init _ = {
people = ["Bob"]
chatLog = []
}
let update msg model =
match msg with
| Speak (person, msg) ->
{ model with chatLog = (sprintf "%s: %s" person msg) :: model.chatLog }
| NewPerson name ->
{ model with people = name :: model.people }
let view model dispatch =
StackPanel.create [
StackPanel.children [
for person in model.people do
Button.create [Button.content $"Say something {person}"; Button.onClick (fun _ -> dispatch (Speak (person, "Blahblahblah")))]
for msg in model.chatLog do
TextBlock.create [TextBlock.text msg]
if model.people.Length = 1 then
Button.create [Button.content "Add Ryan"; Button.onClick (fun _ -> dispatch (NewPerson "Ryan"))]
if model.people.Length = 2 then
Button.create [Button.content "Add Sarah"; Button.onClick (fun _ -> dispatch (NewPerson "Sarah"))]
]
]
Based on https://github.com/fsprojects/Avalonia.FuncUI/issues/369 it looks like the fix is to always specify SubPatchOptions.Always with OnClick.
type Button with
static member onClick logic =
Button.onClick(logic, SubPatchOptions.Always)
Is there a reason why Always isn't already the default?
Good question.
In earlier versions of funcUI elmish was the only programming model. With elmish you end up having one gigantic view tree that you need to diff + patch on every update. In this scenario not patching the click property of 100s of buttons on every updates was a valid argument.
Now for the component model.. I'm not so sure.
@MaxWilson we could make the default configurable.