Shiny Event Chain - Lagged Input Date

Hi,

Im trying to build an event chain for a more complex app, but the problem can be seen in this small example. The idea is that the output of a text field should be given by either:

  1. a textInput, which can be transmitted to the text field by an action button "align"
  2. An action button "set_to_20", which will set first the text input and then the text field to 20

However, no matter what I do, the option 2 does not get transmitted immediately, only after pressing the button "set_to_20" a second time.

Why is this lagged? How to fix it?

Thanks a lot!


library(shiny)
ui <- fluidPage(
  fluidRow(
    column(width= 4,
           textInput(inputId = "input1", label = "Select number of rows", value = "10"),
           actionButton("go","set_to_20"),
           actionButton("align","align_text")
    ),
    column(width = 12,
           verbatimTextOutput(outputId = "Id1"),
           verbatimTextOutput(outputId = "Id2")
    )
  )
)

server <- function(input, output,session) {

   start <- reactiveValues(new=0)
   
  observeEvent("",{
   start$new <- start$new+1
  })
  
  observeEvent(input$go,{
    updateTextInput(session,"input1",value="20")
      })
  
  vals2 <- eventReactive({
    input$go
    input$align
    start$new
        },{
    paste0(input$input1)
  })
  
  output$Id1 <- renderText({
    vals2()
  })
  }

shinyApp(ui,server)

The issue lies in the setup of vals2 as an eventReactive(). If you replace it with a reactive() and include the three reactive values inside the expression, vals2 will depend on any reactive values within the expression --- in other words, it will update its return value when any of input$go, input$align, start$new or input$input1 change.

  vals2 <- reactive({
    input$align
    start$new
    input$go
    paste0(input$input1)
  })

If you want to keep vals2() from changing when input$input1 changes, you can wrap input$input1 in isolate(), i.e.

paste0(isolate(input$input1))
1 Like

If you substitute your output$Id1 as follows I think we can see what's going on:

	output$Id1 <- renderText({
		#vals2()
		paste('vals2:', vals2(), 'input1:', input$input1)
	})

input1 updates as soon as you either enter a new value in the text field or you press the "set_to_20" button. The vals2 reactive seems to first "remember" the current value of the text field and updates to that value as soon as the button is pressed and the action to update the text field gets executed only after vals2 updates.

1 Like

So my previous answer isn't necessary but it highlights the problem. The reason it doesn't update the first time is because updating vals2() doesn't depend on the value of input$input1. The first round of events is triggered by the button click, which updates the text input and triggers an update of vals2(). But then updating input$input1 to 20 triggers a second event where the change is input$input1. At this point, though, input$input1 is isolated from the eventReactive() block, so it's change doesn't trigger an update of vals2().

However, when you click again, the button click updates vals2(), hence updating of the rest of the values.

If you add message("Updating textInput to 20") and similar print statements at the top of each reactive block, you can see the timeline of how the app is updated.

# app start
Previewing vals2() value
# first button click
updating textInput to 20
Previewing vals2() value
Updating vals2() #<< resolves to finish step above
# Second button click
updating textInput to 20
Previewing vals2() value
Updating vals2() #<< resolves to finish step above

Similarly, you'll notice that changing the text input doesn't trigger any reactivity changes in the backend.

2 Likes

Thanks a lot @grrrck and @valeri, that made it a lot clearer.

I was experimenting with a lot of other stuff, but looks like I was missing out on some fundamental understanding of the way the event chain works in this case!

Unfortunately, the solution still does not completely address the issue (I listed only what it should do, not what not :slight_smile: ):

  1. a textInput, which can be transmitted to the text field by an action button "align"
  2. An action button "set_to_20", which will set first the text input and then the text field to 20
  3. No change to the text field should be made by just updating the textinput without pressing the button.

Is that at all possible?
#Addresses 1 and 2 but not 3

  vals2 <- reactive({
    input$align
    start$new
    input$go
    paste0(input$input1)
  })

#Addresses 2 and 3 but not 1

  vals2 <- reactive({
    input$align
    start$new
    input$go
    paste0(isolate(input$input1))
  })

Ok using the comments here and a post on SO, i finally put together the right solution.

Thanks all!

library(shiny)
ui <- fluidPage(
  fluidRow(
    column(width= 4,
           textInput(inputId = "input1", label = "Select number of rows", value = "10"),
           actionButton("go","set_to_20"),
           actionButton("align","align_text")
    ),
    column(width = 12,
           verbatimTextOutput(outputId = "Id1")
    )
  )
)

server <- function(input, output,session) {
  
  start <- reactiveValues(new=0)
  
  observeEvent("",{
    start$new <- start$new+1
  })
  v <- reactiveValues(input1=NULL)
  
  observe(
    {
      v$input1 <- isolate(input$input1)
    }, priority = 10)
  
  observeEvent(input$go,priority=1,{
    updateTextInput(session,"input1",value="20")
    v$input1 <- "20"
  })
  
  observeEvent({
    input$align
    start$new
    input$go},priority = 5,{
    v$input1 <- isolate(input$input1)
    paste0(isolate(input$input1))
  })
  
  output$Id1 <- renderText({
    paste('input1:', input$input1,"vinz",v$input1)
  })
}


shinyApp(ui,server)

I really don't understand what's going on your app — you seem to have a observeEvent() and a observe() that don't actually listen to any changes. It sounds like you want to have a cached value that is only updated when the user clicks either of two buttons, so I implemented that from first principles below:

library(shiny)
ui <- fluidPage(
  numericInput("n", "n", value = 10),
  actionButton("set_20", "set 20"),
  actionButton("update_cache", "update cache"),
  verbatimTextOutput("desc")
)

server <- function(input, output, session) {
  cache <- reactiveValues(n = 10)
  
  observeEvent(input$set_20, {
    updateTextInput(session, "n", value = 20)
    cache$n <- 20
  })

  observeEvent(input$update_cache, {
    cache$n <- input$n
  })
  
  output$desc <- renderText({
    paste("input: ", input$n, "\ncache: ", cache$n)
  })
}

shinyApp(ui, server)

(I'd recommend using a coding style that consistently indents the contents of {}; that makes it much easier to see the structure of your code at a glance)

Hi @hadley,

thanks, that is a much clearer and straightforward solution!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.