Persistent `.recalculating` class if removeUI and output hiding operations are performed
gsmolinski opened this issue · 4 comments
System details
R version 4.4.1 (2024-06-14 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 10 x64 (build 19044)
Matrix products: default
locale:
[1] LC_COLLATE=Polish_Poland.utf8
[2] LC_CTYPE=Polish_Poland.utf8
[3] LC_MONETARY=Polish_Poland.utf8
[4] LC_NUMERIC=C
[5] LC_TIME=Polish_Poland.utf8
time zone: Europe/Warsaw
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets
[6] methods base
other attached packages:
[1] reactable_0.4.4 shiny_1.9.1
loaded via a namespace (and not attached):
[1] cli_3.6.3 rlang_1.1.4
[3] promises_1.3.0 jsonlite_1.8.8
[5] xtable_1.8-4 listenv_0.9.1
[7] htmltools_0.5.8.1 httpuv_1.6.15
[9] reactR_0.6.0 sass_0.4.9.9000
[11] crosstalk_1.2.1 jquerylib_0.1.4
[13] fastmap_1.2.0 yaml_2.3.8
[15] lifecycle_1.0.4 memoise_2.0.1
[17] compiler_4.4.1 codetools_0.2-20
[19] htmlwidgets_1.6.4 Rcpp_1.0.12
[21] rstudioapi_0.16.0 future_1.33.2
[23] later_1.3.2 digest_0.6.36
[25] R6_2.5.1 parallelly_1.37.1
[27] parallel_4.4.1 magrittr_2.0.3
[29] bslib_0.7.0 tools_4.4.1
[31] withr_3.0.0 mime_0.12
[33] globals_0.16.3 cachem_1.1.0
Example application or steps to reproduce the problem
To reproduce the problem:
- Run app.
- Click "Hide"
- Click "Remove UI"
- Click "Show"
- Change input text to something different
You will see persistent busy indicator.
library(shiny)
ui <- fluidPage(
tags$script(HTML("
$( document ).ready(function() {
Shiny.addCustomMessageHandler('show', function(e) {
let result = document.getElementById('text_1');
result.style.removeProperty('display');
});
});
")),
tags$script(HTML("
$( document ).ready(function() {
Shiny.addCustomMessageHandler('hide', function(e) {
let result = document.getElementById('text_1');
if (result != null) {
result.style.display = 'none';
}
});
});
")),
useBusyIndicators(),
div(id = "my_div"),
textInput("text_input", "Text", value = "test"),
actionButton("hide_btn", "Hide"),
actionButton("rm_btn", "Remove UI"),
actionButton("show_btn", "Show")
)
server <- function(input, output, session) {
observe({
insertUI("#my_div", ui = textOutput("text_1"))
insertUI("#my_div", ui = textOutput("text_2"))
})
observe({
removeUI("#text_2")
output[["text_2"]] <- NULL
}) |>
bindEvent(input$rm_btn)
observe({
session$sendCustomMessage("hide", "placeholder")
}) |>
bindEvent(input$hide_btn)
observe({
session$sendCustomMessage("show", "placeholder")
}) |>
bindEvent(input$show_btn)
output$text_1 <- renderText({
input$text_input
})
output$text_2 <- renderText({
"this line will be removed"
})
}
shinyApp(ui, server)
Describe the problem in detail
I'm working on the app where user can create outputs dynamically, remove them, hide them and show them; and above I have showed as minimal reproducible example as I was able to produce based on my app.
Problem is that busy indicator (class .recalculating
) is persistent if UI removing (of output X) and UI hiding (of output Y) is used (both of these operations must be used and in the order I mentioned above). Specifically, problem is when output X is removing and at the same time output Y has style display = 'none'
.
I would like to say that in the previous version of shiny
this problem does not occur, but this is somehow more complicated. In my real app this problem does not occur in previous version, but using this MRE I see the similar problem (of course, busy indicator is not present, but output is not updated in previous version where busy indicators were not introduced). I can't say why in my real app this problem was not present in previous version.
I believe this is a bug in {shiny}
, but if someone could explain me, why this problem exists and what can I do with that, that would be great :).
I haven't fully investigated this yet, but it also seems important to note that the behavior of this app doesn't seem correct with shiny v1.8.1.1 either (before busy indicators and changes to .recalculating
). With that version of shiny (v1.8.1.1), and by following the steps outlined here, one would expect the text_1
output to update when the input changes, but it doesn't:
bug.mp4
So, I'm not yet ready to say the new behavior is "wrong" to label the output as recalculating, but is more likely highlighting a problem that already existed
Ok, I've investigated the issue further, and it's due to the fact that the show/hide methods used here are changing the visibility of text_1
without letting Shiny know about that change. You can fix that issue by updating your show message handler to:
Shiny.addCustomMessageHandler('show', function(e) {
let result = document.getElementById('text_1');
result.style.removeProperty('display');
Shiny.bindAll(result)
});
Count this as yet another reason why Shiny should be leveraging IntersectionObserver()
and ResizeObserver()
to automatically send visibility and size changes to the server. There's a draft for this in #3682
Thank you very much! I wasn't aware that it is need to call bindAll()
.