shiny
shiny copied to clipboard
Deprecate freezeReactiveVal, and freezeReactiveValue for non-inputs
Consider getting rid of freezeReactiveVal
, and freezeReactiveValue
for non-input
objects. If you're using these functions and seeing a deprecation message, please add a comment to this issue--we want to know what you're doing!
The need for freezeReactiveValue(input, "x")
is clear. When server
calls for changes to an input on the client side, via renderUI
with inputs inside, or updateXXXInput
, or insertUI
, it's likely that the input's current value is, temporarily, no longer useful or even dangerous (in that it might be inconsistent with other input state that has already been updated). freezeReactiveValue
prevents outputs from rendering an error during this window of time.
The need to use freezeReactiveVal
, or freezeReactiveValue
with a generic reactiveValues
object, is far less clear. I think we implemented it for "consistency" but there's nothing consistent about these use cases, and it's not clear to me that you could effectively use them today if you wanted to (especially, the "automatically thaw on onFlushed
" seems wrong to me now).
We could fix the semantics of these functions so they can actually be used, but I'm inclined to proceed cautiously because 1) I'm still not sure what scenario exists where you'd actually want to use it, and 2) in case someone has somehow figured out a way to use the current implementation to do something useful, we'd be breaking them.
I propose we soft-deprecate these uses, and have the deprecation warning message include a link to this issue. If people are actually using these functions then we can consider un-deprecating.
FYI a GitHub search for "freezeReactiveVal" in R code resulted in 0 hits outside of shiny source code. For reference, "freezeReactiveValue" did have real usages found on GitHub. This isn't conclusive evidence, but helps support your claim that it probably isn't used much
https://github.com/search?l=R&q=freezeReactiveVal&type=Code
I'll admit this is not something used a lot in shiny but I have a lot of apps that follow this pattern.
I make the update client-side and want to keep the underlying reactive in-sync but not re-render the whole thing.
library(shiny)
js <- "
const deleteListen = () => {
$('.delete').off();
$('.delete').on('click', (e) => {
// get parent
let $el = $(e.target).closest('.el');
// get row index and remove
let index = $el.data('index');
$el.remove();
// send to server
Shiny.setInputValue('deleted', index);
});
}
Shiny.addCustomMessageHandler('update-data', (msg) => {
console.log(msg);
let cnt = '';
msg.forEach((row, index) => {
cnt += `<div data-index='${index}' class='el'>
<span>Speed: ${row.speed}</span>
<span>Dist: ${row.dist}</span>
<button class='delete'>delete</button>
</div>`;
});
$('#ui').html(cnt);
deleteListen()
});
"
ui <- fluidPage(
tags$head(
tags$style(
".el {
border:1px solid black;
padding:1rem;
margin:1rem;
}"
),
tags$script(HTML(js))
),
div(id = "ui")
)
server <- function(input, output, session) {
cars_r <- reactiveVal(cars)
# 1. need to be inside observe to send message
# 2. this reactive may be updated elsehwere
# in the app: then we want to re-render
observeEvent(cars_r(), {
session$sendCustomMessage(
"update-data",
apply(cars_r(), 1, as.list)
)
})
observeEvent(input$deleted, {
# freeze
# I don't want to trigger the observe event above
# and re-render everything
# no need it's already removed from UI
freezeReactiveVal(cars_r)
new_cars <- cars_r()[-input$deleted]
})
}
shinyApp(ui, server)
I know we can achieve something like this with the user of another reactive to selectively trigger the rendering but this can become very messy (I find) for larger applciations.
Any suggestion to avoid freezing anything here is most welcome!
@JohnCoene: Can you avoid freezing with two cars
reactive values: one that holds the value on input (and that is watched to update the UI), and one that holds the "true" value? The second reactive value would be updated whenever the input changes or the user presses the "delete" button.
I have 2 views on my data, one as datatable(DT) and the other graphical(plotly). Stuff happens by selecting a object(cell) in the table. I want to allow user to select using the graph and I am sending the object name with Shiny.setInputValue js. My first attempt is to update input$dttable_cells_selected but that is not allowed. To make the story short, I add a layer of reactiveValues. When the user clicks an object in the graph, I need to udpate the table on top of all the other actions because the selected object may not be on the current page (DT::selectCells/DT::selectRows are used).
My implementation is not stable as I am seeing asserting failures -- object cannot be located in the table unexpectedly.
Btw, I have been doing more serious work with shiny and R for only a few months(start using env and did not know the difference between pandas and R dataframe a few months back) and know virtually nothing about html/js/css. I am comfortable with reactive programming because I do complex pipelines with gnu make for many years. I suspect the issue is that shiny dispatch mechanism is using 'input' snapshot(including reactive expressions I created and updated in the current cycle) -- where with make I see the updates immediately from the file system.
I think I may have to 'freeze' the additional layer of reactiveValues(outside input) to ensure the datatable view is updated before the actions I normally run by selecting the table cell. I do not know if I am on the right path and have yet to dive into what is really happening with the crash/assertion failure.
In summary:
plotly input | table input -> non-input reactiveValues -> action based on table selection
|
| --> freezing + update table selection
@GDCCP I'd suggest to split up the issue library-wise {DT}/{plotly} to create minimal reproducible examples and post them here or here. Cheers
Noted. However, I probably cannot have a working implementation soon, or ever. Try to summarize briefly here.
This is a server DT table and actions are triggered cell/row selection, i.e. changing variables like input$table_rows_selected and input$table_rows_current
The enhancement is to tigger the action by clicking an object in a plotly graph (which maps to a string in say input$fig_plotly_clicked). I was trying to map this string back to a valid row number and make the call to DT::selectRows using the tableProxy, and trigger a change in the input$table_rows_selected and input$table_rows_current, I imagine, in my calls to DT::selectRows and DT::selectPage (without explict reading anything related to the DT table). I am also using DT filter='top' option, and numeric page input. I am now wondering if my use case is really supported.
@GDCCP you might be interested in library(crosstalk). However, still I'd encourage you to create a separate post.
- A cool example in the anwser https://stackoverflow.com/questions/70843664/is-there-a-way-to-prevent-observeevent-from-triggering-while-still-entering-te