I routinely build dynamic UIs in my Shiny apps via
removeUI(). Those dynamic UI elements will then often contain input elements, which I may then also want to update via
updateXYZ() (i.e., really, via
sendInputMessage()). For complex data-dependent custom inputs, the pattern will be to insert that input into the DOM, but it won't be useful until it's been updated based on the current state of some data server-side. (In essence, the inserted nascent input element is something of a useless 'shell' until it receives its first update from the server.)
One way to prevent a race condition (where the update happens before the input element is bound) is to listen for the new input via something like:
observeEvent(input$myInput, updateXYZ("myInput", ...))
... whereby the update occurs in a later reactive flush cycle.
In the pursuit of understanding nuance, however, I'm curious if there's any safe way to do this within the same reactive flush cycle.
In particular, I've had trouble finding documentation on any guaranteed ordering of server -> client messages in each reactive flush.
output messages sent first, then
Or is the messaging ordering non-guaranteed?
And likewise is there any guarantee on the ordering of the client-side handling?
I imagine providing strict guarantees client-side is somewhat tricky, since even if a message that adds an element to the DOM is handled first, the input binding's
find() callback would now need to run, detect the new input element, run the binding's
initialize() and then bind all before handling the
updateXYZ() message contents. (I.e. this involves either some synchronous patterns or careful task priority queue management.)
I've actually inferred a bit from the codebase itself, but I don't know if some patterns there are intentional and come with long-lasting guarantees, which is why I'm trying to find explicit architectural documentation instead.
It's probably worth mentioning that I've already come up with (and tested) a few other patterns to provide some ordering-guarantees in my apps already, but I'm still trying to find good documentation on the R message-sending process and the JS message-handling process. Some of those other patterns are (very) briefly:
By managing some client-side state in your inputs, you can use
Shiny.setInputValue() to send a special "ready" message to a different input slot on the R side. How do you know when your input is ready? Listen for the
shiny:bound event (and possibly the
shiny:connected event, if
Shiny.shinyapp.isConnected() is currently
false). The input on the R side can have a name like
input$myInput__ready and the contents of that ready beacon message can be the client-side
Date.now(), roughly guaranteeing a new "ready" signal each time the input is bound.
Similar to the above, one can send an initial "ready" input value ( by overloading the binding's
getValue() method) that indicates the element is bound but has not yet handled its first update via
receiveMessage(). Observers on the R side would then need to differentiate between the "ready" value and a 'standard' input value. This has the advantage of guaranteeing boundness/connectedness (since it's using
getValue()) without registering new event listeners, but has the additional complexity R-side of needing to conditionally handle different input values. This is more-or-less equivalent to the
observeEvent(input$myInput, ...) example above, but explicitly helps to differentiate between when the input is first ready vs has-received its first
updateXYZ(), which is when it might be truly ready/useful in the app context. (Best used with
shiny::registerInputHandler() to help with that conditional logic.)