Genie Discord forum

Author AvatarMish_
10/17/2023, 7:51:30 AM

Hi!

I am quite new to Julia and this is my first time using Genie. So far I quite like it, but I am struggling with something at the moment. I want to use structs in my app, but I am encountering some unexpected behavior in the reactivity/communication of objects between the back-end and the UI. I included a simple example to illustrate my problem. I created a testDict and a testStruct containing an x and y variable. The UI has a button that increments both xs in the UI and a button that increments both ys in the back-end. Some observations:

  1. The @onchange for incrementing x is triggered on the testDict, but not on the testStruct
  2. Incrementing y is not communicated to the UI for either testDict or testStruct

I didn't find any up-to-date examples on this using the ReactiveTools API, so decided to post here. Any ideas on how to get this to work? Is this even supported?

using GenieFramework
@genietools

mutable struct demo
    x::Number
    y::Number
end

@app begin
    @in testDict = Dict("x" => 1, "y" => 2)
    @in testStruct = demo(1, 2)
    @event :printtest begin
        @show testDict
        @show testStruct
    end
    @event :incrementy begin
        testDict["y"] += 1
        testStruct.y += 1
    end
    @onchange testDict begin
        @show testDict
    end
    @onchange testStruct begin
        @show testStruct
    end
end

function ui()
    [
        row([p("Dict:{{testDict}}")]),
        row([p("Struct:{{testStruct}}")]),
        row([
            btn("Increment X", @on("click", "testDict.x++; testStruct.x++")),
            btn("Increment Y", @on("click", :incrementy)),
            btn("Show in console", @on("click", :printtest)),
        ])
    ]
end

@page("/", ui)

up()
Author AvatarPere
10/17/2023, 1:47:00 PM

When you declare a reactive variable, the entire variable is reactive. This means that handlers will only be triggered when its value is changed, not when one of its field changes. For instance, if you define a button that modifies the first entry in an array like this:

@app begin
    @in array = [1,2,3,4]
    @in show_array = false
    @onchange array begin
        @show array
    end
    @onbutton show_array begin
        @show array
    end
end

ui = [btn("Increase count", @click("array[0] += 1")), btn("Show in REPL", @click(:show_array)), "{{array}}"]
@page("/", ui)

You'll see that when clicking "Increase count" the array in the page does not change, and that the handler is not triggered and therefore no message is shown in the repl. If you click the "show in repl" button, the REPL will show 1,2,3,4, which indicates that no modification took value.

If you want to trigger the handler, you need to change the entire array as

ui() = [btn("Click me", @click("array = [4,5,6,7]")), btn("Show in REPL", @click(:show_array)), "{{array}}"]
Author AvatarPere
10/17/2023, 1:48:37 PM

Also, another thing. The testDicthandler was triggered in your example because you are firing the :incrementyevent. I'm not sure it'd work if you were to just modify the Dict inside the button @on event handler.

Hope this helped!

Author AvatarMish_
10/17/2023, 2:55:49 PM

Thanks for your response!

Ah ok, I see... The array elements indeed behave the same way as the struct fields. Interesting that a Dict does seem to have reactive fields, because the "increment x" button does trigger the testDict handler for me (see screenshot). Also the representations in Vue devtools of testDict and testStruct look identical, so it does seem to me there is a difference in how Dicts and structs are handled. Which makes sense from an implementation point of view, but at least from the Vue side it seems to be possible to have reactive object fields/elements.

But does this mean in general that "recursive" reactivity, i.e. react to fields/elements of reactive objects changing, is not supported? If so, are there any plans to support that in the future? To me that would make the framework much more powerful, allowing for more complicated interactions.