New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bidirectional synchronization triggers back and forth fight between transactions #2767
Comments
A possible strategy to handle this is to add a "sync" strategy for the inputs. Currently in input_rate there's throttling and debounce. One could envision a synchronized that will only send the update event if the state is not busy. It requires that the current eventhandler for busy sets a flag on the shiny app and then this state is exported and available by the synchronized input_rate. |
Depending on the complexity of the value you are loading you could use In the example below, the first time the button is pressed the library(shiny)
shinyApp(
ui = fluidPage(actionButton("inc", "Increment")),
server = function(input, output) {
state <- reactiveValues(
count = 1
)
observeEvent(state$count, {
cat("Count: ", state$count, "\n")
})
observeEvent(input$inc, {
cat("Button: ", input$inc, "\n")
state$count <- as.numeric(input$inc)
})
}
) It sounds like the objects you are dealing with are more complex, so perhaps there's a way you could instead store a unique id in a |
@nteetor I am already using reactiveValues, and it's not the reason why this behavior occurs. The behavior occurs because:
If during this transaction before the state is back to idle, another event is generated (e.g. the user types in, and the throttle of 250 ms is over), the sequence will become
The problem here is that there should not be the possibility to generate another event until the first one has done the full round trip from client to server back to client. Only then, a new event can be sent. This is the way desktop UIs solve the issue, but in Shiny design the UI state and the model state are separated. |
Hello again, I was not trying to trivialize your problem. We've run into this duplication of reactivity a frustrating amount of times at my group, so I was hoping to help. I did illustrate my workaround poorly. Here is an updated version of the server <- function(input, output, session) {
state <- reactiveValues(
name = NULL
)
app_model <- AppModel()
output$checklistUi <- renderUI({
# Important. must be slow.
Sys.sleep(2)
completion_widget <- "Hello"
completed <- AppModel_checkCompleteness(app_model)
return(completion_widget)
})
# !! important, take the input value and store it in a reactive value
observeEvent(input$nameText, {
state$name <- input$nameText
})
# !! important, no longer observe input$nameText, now observe state$name
observeEvent(state$name, {
cat("Updating app model name\n")
app_model$name <- input$nameText
})
observeEvent(app_model$name, {
cat("Updating browser text input\n")
updateTextInput(session, "nameText", value = app_model$name)
})
} Before my changes to the server I did observe the problem you described. Afterwards I no longer saw the repeated updates. |
@nteetor I tried your solution but I see no change. I suspect that what happens is that by adding a variable, you introduce enough delay for the transaction to complete. In desktop UI programming the loop is always closed and extinguished because you can't process a new event before the old one has been fully processed. |
I’m sorry to hear that. I’m eager then for a more comprehensive solution from others or the shiny team. |
Hi @stefanoborini , tricky problem, which I also keep coming across. I'm still looking for a better solution, but for now you could do this:
|
System details
Browser Version: Chrome 80.0.3987.116 on macOS.
Output of
sessionInfo()
:Example application or steps to reproduce the problem
Describe the problem in detail
The problem occurs when bidirectional synchronization between a model and a UI element is needed (e.g. one has to reload model data from the disk, but also allow the user to modify it) and the transaction is slow. More details can be found on this post on the Shiny help forum.
The problem happens when the textinput ui is modified by the user, then modified again before the transaction to update the UI is completed. Taking a look at the websocket traffic, it's evident that during the second modification, a new request is sent to the server.
Later, the server replies to the first request, but this reply also contains, in the inputMessages field, the old value for the textinput. This sets the textinput to the old value, which generates a new request before the server has replied to now the second. This starts a cycle of bouncing back and forth between the two requests.
In my opinion, this is a bug that occurs because the frontend is sending a second request before the first transaction is completed. The UI should not accept any input until the current in-progress transaction goes back from busy to idle.
The text was updated successfully, but these errors were encountered: