Differences between observeEvent and eventReactive

Hello. I'm trying to understand the differences between using observeEvent and eventReactive to express how the app should react to input changes. By reading the documentation + the similar APIs, it seems that these functions would be called only when something happens with the first input, eventExpr ("something happens" = in case it's an usual user input, it's changed; in case it's a button, it's pressed again, and so on). In this example, observeEvent appears to not follow this pattern:

library(shiny)

expr_output <- function(x)
  paste(capture.output(eval(parse(text = x))), collapse = '\n')

ui <- fluidPage(
  column(
    width = 6,
    textAreaInput('req', 'Enter code'),
    actionButton('sent', 'Send')
  ),
  
  column(
    width = 6,
    h2('separate recalculation from updating output'), verbatimTextOutput('res1'),
    h2('all "at once"'), verbatimTextOutput('res2')
  )
)

server <- function(input, output, session) {
  res1 <- eventReactive(input$sent, expr_output(input$req))
  output$res1 <- renderText(res1())
  
  observeEvent(input$sent, {
    output$res2 <- renderText(expr_output(input$req))
  })
}

shinyApp(ui, server)

The following steps reproduce the behavior I find counter intuitive:

  1. Enter x <- 1; x + 1 in the text input area;
  2. Press Send
  3. Change the text input to x <- 1; x + 2

The version with observeEvent is updated even though the button was not pressed again. I infer (because I didn't look at the code yet) that observeEvent will listen to changes not only in eventExpr but also for all reactive values present in handlerExpr. Is that the intended behavior? The documentation makes clear the distinction between events and actions (and in this case eventReactive does solve the problem by splitting the two steps), but this part doesn't make very clear that inputs in handlerExpr are also being listened:

?observeEvent

Use observeEvent whenever you want to perform an action in response to an event. (Note that "recalculate a value" does not generally count as performing an action–see eventReactive for that.) The first argument is the event you want to respond to, and the second argument is a function that should be called whenever the event occurs.

Thanks in advance.

Hi,

They are identical but for one thing:

  • observeEvent() just executes code within it's boundaries but does not assign the output to anything
  • eventReactive() will save the output to a reactive variable

Both of them will only execute code code if the condition at the top is satisfied. That's where they differ from reactive() that will run anytime any of the reactive values within change.

So you use observeEvent with code that does not produce an output that you capture in a reactive variable, and eventReactive when you do. On the other hand, you can simulate the effect of one with the other.

EXAMPLE

observeEvent(input$button1, {
 showModal(modalDialog("You clicked the button!"))
})

EXAMPLE

myVar = eventReactive(input$button1, {
  Sys.time()
})

output$myText = renderText({
  paste("You clicked the button at", myVar())
})

This is the longer version with observeEvent instead of eventReactive

clickTime = reactiveVal()
observeEvent(input$button1, {
  clickTime(Sys.time())
})

output$myText = renderText({
  paste("You clicked the button at", clickTime())
})

Hope this helps,
PJ

1 Like

Hi @abelb0rges,

if you change your observeEvent to

 observeEvent(input$sent, {
        txt <- expr_output(input$req)
        output$res2 <- renderText(txt)
        
        # more elegant alternative
        output$res2 <- renderText(expr_output(isolate(input$req)))
    
    })

you get the desired behaviour.

To my understanding, the crucial point is the following:
In your observeEvent you connect an output value to a reactive input value, i.e. at the first click of the button the connection is established and then persists in the reactive dependency graph forever. In your source code, both lines with output$res1/2 <- ... are qualitatively identical, i.e they both connect two reactives; res1 is a reactive expression while input$req$ is a reactive value.

You can break the connection by inserting a non-reactive variable or by using isolate.

Hope that helps...

2 Likes

Yes, this does make sense. Now it’s clear. Thanks a lot!

Hey, @pieterjanvc, thanks for taking the time to elaborate a bit on that. I believe @smichal addressed the issue with my sample app.

I edited my solution and added the more elegant isolate way.

1 Like

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

If you have a query related to it or one of the replies, start a new topic and refer back with a link.