-
I'm a bit curious as to why this doesn't work how I might think it would. In Vue it's pretty trivial to create a state store with their use of signals. You can copy the signal around the app because the signal has a reference pointing to a value. Then when the value is updated the changes are propagated throughout the app. Sadly I've come to find that implementing a state store in Avalonia/FuncUI has been very challenging. It seems very difficult to get components to become reactive to state changes across the app. It seems like the only way to force updates is by making up a key that will change. type ItemStore =
{ Items : IWritable<Item array>
Selected : IWritable<Request option> }
member this.updateRequest(newRequest : Request) =
this.Items.Set { newRequest with updatedAt = DateTime.Now }
[<RequireQualifiedAccess>]
module StateStore =
let itemStore = ItemStore.init ()
//...
let view =
Component (fun ctx ->
let selected = ctx.usePassedRead StateStore.itemStore.Selected
let items = ctx.usePassedRead StateStore.itemStore.Items
Grid.create
[ Grid.columnDefinitions "*, 4, 4*"
Grid.children
[
ContentControl.create [
Grid.column 0
ContentControl.content (Sidebar.view items)
]
GridSplitter.create [ GridSplitter.background "White"; GridSplitter.column 1; Grid.column 1 ]
ContentControl.create [
Grid.column 2
ContentControl.content (RequestEditor.view selected)
]
]]) I do wonder that maybe this pattern is okay, but only works if the option type is forcing me to break the chain? I did the below because it was annoying to check the option at every step in the application. // RequestEditor module
let view (selected : IReadable<Request option>) =
let viewId =
match selected.Current with
| Some id -> id.ToString ()
| None -> "none"
Component.create (
$"editor-main-{viewId}",
fun ctx ->
let selected = ctx.usePassedRead selected
DockPanel.create
[ DockPanel.children
[ match selected.Current with
| Some request -> requestEditor (ctx.useState request)
| None -> emptyDisplay () ] ]
) EDIT: Turns out I was kinda right on how unraveling the option and creating a new state breaks the reactivity that I was expecting. I could create a function to deal with the boilerplate, but maybe there's a simpler approach? I've also struggled with lists and arrays. I want to unwrap them, but it seems like I have to do some hacky things with keys to make the state properly update. Edit 2: I think my biggest question now is how to maintain reactivity for non trivial solutions. A few I ran into that felt difficult were unwrapping options, list items, and discriminate unions where I want to render something based on the type found. Like in my code I have |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
Good questions overall. I recommend looking at the "Todo App" Sample project. It deals with a lot of the problems you describe above. If you still have questions after that let me know. |
Beta Was this translation helpful? Give feedback.
I get all the issues you describe above. For some of them I found good solutions, but not for all. It's quite hard to make all the transformations without breaking the reactive chain.
I'm wondering if it would have been better to base the state implementation on
IObservable
+IObserver
, but what's missing is a way to obtain the current value.Option type
Yeah, thats true. You can solve that by combining
State.filter
andState.map
. Maybe there should be aState.choose
.Discriminated Unions
Yeah, it's tricky.
Code below is untes…