Note: After coming up with the answer I reworded the question to make if clearer.
Sometimes in a shiny
app. I want to make use of a value selected by the user for a widget, as well as the previous value selected for that same widget. This could apply to reactive values derived from user input, where I want the old and the new value.
The problem is that if I try to save the value of a widget, then the variable containing that value has to be reactive or it will not update every time the widget changes. But, if I save the the value in a reactive context it will always give me the current value, not the previous one.
How can I save the previous value of a widget, but still have it update every time the user changes the widget?
Is there a way that does not require the use of an actionButton
every time the user changes things? Avoiding an actionButton
can be desirable with adding one is otherwise unnecessary and creates excess clicking for the user.
Seeing as the session flush event method seems to be broken for this purpose, here is an alternative way to do it using an observeEvent
construct and a reactive variable.
library(shiny)
ui <- fluidPage(
h1("Memory"),
sidebarLayout(
sidebarPanel(
numericInput("val", "Next Value", 10)
),
mainPanel(
verbatimTextOutput("curval"),
verbatimTextOutput("lstval")
)
)
)
server <- function(input,output,session) {
rv <- reactiveValues(lstval=0,curval=0)
observeEvent(input$val, {rv$lstval <- rv$curval; rv$curval <- input$val})
curre <- reactive({req(input$val); input$val; rv$curval})
lstre <- reactive({req(input$val); input$val; rv$lstval})
output$curval <- renderPrint({sprintf("cur:%d",curre())})
output$lstval <- renderPrint({sprintf("lst:%d",lstre())})
}
options(shiny.reactlog = TRUE)
shinyApp(ui, server)
Yielding:
Update This answer was posted before the advent of the reactiveValues
/observeEvent
model in shiny
. I think that @MikeWise 's answer is the better way to do this.
After some playing around this is what I came up with. The ui.r is nothing special
ui.r
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId="XX", label="Choose a letter",choices=letters[1:5])
),
mainPanel(
textOutput("Current"),
textOutput("old")
)
)
))
"Current"
will display the current selection and "old"
displays the previous selection.
In the server.r
I made use of three key functions: reactiveValues
, isolate
and session$onFlush
.
server.r
library(shiny)
server <- function(input, output,session) {
Values<-reactiveValues(old="Start")
session$onFlush(once=FALSE, function(){
isolate({ Values$old<-input$XX })
})
output$Current <- renderText({paste("Current:",input$XX)})
output$old <- renderText({ paste("Old:",Values$old) })
}
The server.r
works like this.
First, Values$old
is created using the reactiveValues
function. I gave it the value "Start" to make it clear what was happening on load up.
Then I added a session$onFlush
function. Note that I have session
as an argument in my server
function. This will run every time that shiny flushes the reactive system - such as when the selectizeInput
is changed by the user. What is important is that it will run before input$XX
gets a new value - so the value has changed at the selectizeInput
but not at XX
.
Inside the session$onFlush
I then assign the outgoing value of XX
to Values$old
. This is done inside an isolate()
as this will prevent any problems with input$XX
gets updated with the new values. I can then use input$XX
and Values$old
in the renderText()
functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With