What's the Difference Between Reactive Value and Reactive Expression

What's the difference between Reactive Value and Reactive Expression?

Using
currentFib <- reactiveValues({ fib(as.numeric(input$n)) }) will not work in this context.
You will get an error saying that you are accessing reactive values outside of the "reactive context."

However, if you wrap it inside a function call instead, it will work:

currentFib <- function(){ fib(as.numeric(input$n)) }

This works because now the function call is inside a reactive context.

The key difference is the distinction they make in the Shiny documentation, between reactive "sources" and "conductors." In that terminology, reactive({...}) is a conductor, but reactiveValues can only be a source.

  • Here's how I think of reactiveValues - as a way to extend input which gets specified in UI.R. Sometimes, the slots in input are not enough, and we want derived values based on those input slots. In other words, it is a way to extend the list of input slots for future reactive computations.

  • Reactive() does what you say -- it returns the value, after re-running the expression each time any reactive Value changes. If you look at the source code for reactive you can see it:
    The last line is that cached value that is being returned: Observable$new(fun, label)$getValue where 'fun' is the expression that was sent in the call to reactive.

R Shiny: reactiveValues vs reactive

There is a catch, though it won't come into play in your example.

The shiny developers designed reactive() to be lazy, meaning that the expression contained in it will only be executed when it is called by one of its dependents. When one of its reactive dependencies is changed, it clears its cache and notifies its own dependents, but it is not itself executed until asked to by one of those dependents. (So if, say, its sole dependent is a textOutput() element on a hidden tab, it won't actually be executed unless and until that tab is opened.)

observe(), on the other hand, is eager; the expression that it contains will be executed right away whenever one of its reactive dependencies is changed -- even if it's value is not needed by any of its dependents (and in fact even if has no dependents). Such eagerness is desirable when you're calling observe() for its side-effects, but it can be wasteful when you're only using it to pass on the return value of its contents to other reactive expressions or endpoints down the line.

Joe Cheng explains this distinction quite well in his 2016 Shiny Developer Conference presentation on "Effective reactive programming", available here. See especially the bit starting around 30:20 in the presentation's second hour. If you watch until 40:42 (blink and you'll miss it!) he briefly characterizes the behavior of the observe()/reactiveValue () combination that you like.

Advantages of reactive vs. observe vs. observeEvent

First off this stuff is sort of ambiguous, and not very intuitive in some ways, it even says so on the Shiny blog!

Here is my best understanding of the topic..

Lets start with reactive

The reactive function allows a user to monitor the status of an input or other changing variable and return the value to be used elsewhere in the code. The monitoring of a reactive variable is considered lazy, "Reactive expressions use lazy evaluation; that is, when their dependencies change, they don't re-execute right away but rather wait until they are called by someone else.(Source)". You show this well in example 2, as you can call the variable inside the renderText environment, once called the code inside the reactive call executes and re-evaluates the variable.

For science nerds, this is a lot like quantum mechanics in that by calling the reactive variable (observing it) causes it to change by re-evaluating, too much of a stretch?

Now to observe

Observe is similar reactive, the main difference is that it does not return any values to any other environment besides its own, and it is not lazy. The observe function continually monitors any changes in all reactive values within its environment and runs the code in it's environment when these values are changed. So, observe is not a "lazy" evaluation since it does not wait to be called before it re-evaluates. Again note that you cannot assign variables from observe.

For sake of experiment:

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

observe({
if (input$choice == 'Hello') {
getStatus <- 'Hi there'
}
})

observe({
if (input$choice == 'Goodbye') {
getStatus <- 'See you later'
}
})

output$result <- renderText({
getStatus
})

})

shinyApp(ui = ui, server = server)

Sample Image

What is important to notice is that during the code executed in observe, we can manipulate outside environment reactive variables. In your case you assign text <- reactiveValues() and then manipulate that by calling text$result <- 'Hi there'. We can also do things like update selectInput choices, or other shiny widgets, but we cannot assign any non-reactive variables in this environment like our getStatus in the example above. And this idea is mentioned on the observe documentation,

"An observer is like a reactive expression in that it can read reactive values and call reactive expressions, and will automatically re-execute when those dependencies change. But unlike reactive expressions, it doesn't yield a result and can't be used as an input to other reactive expressions. Thus, observers are only useful for their side effects (for example, performing I/O)(Source)"

Lastly, observeEvent

The best way to use observeEvent is to think of it as a defined trigger, as in it watches one event or change in a variable, and then fires when the event happens. I most commonly use this to watch input to buttons, as that is a defined event in which I want things to happen after the button is pushed. It uses an isolate environment which I think is the perfect name for how this function works.

Inside this environment we can call a bunch of reactive variables, but we only define one as the trigger. The main difference between observeEvent and observe being the trigger, as observe runs anytime anything changes, and observeEvent waits for the trigger. Note that this environment is similar to observe in that it does not return non-reactive variables.

Summary

Here is an example that brings all these ideas together:

library(shiny)

ui<-
fluidPage(
fluidRow(
column(4,
h2("Reactive Test"),
textInput("Test_R","Test_R"),
textInput("Test_R2","Test_R2"),
textInput("Test_R3","Test_R3"),
tableOutput("React_Out")
),
column(4,
h2("Observe Test"),
textInput("Test","Test"),
textInput("Test2","Test2"),
textInput("Test3","Test3"),
tableOutput("Observe_Out")
),
column(4,
h2("Observe Event Test"),
textInput("Test_OE","Test_OE"),
textInput("Test_OE2","Test_OE2"),
textInput("Test_OE3","Test_OE3"),
tableOutput("Observe_Out_E"),
actionButton("Go","Test")
)

),
fluidRow(
column(8,
h4("Note that observe and reactive work very much the same on the surface,
it is when we get into the server where we see the differences, and how those
can be exploited for diffrent uses.")
))

)

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

# Create a reactive Evironment. Note that we can call the varaible outside same place
# where it was created by calling Reactive_Var(). When the varaible is called by
# renderTable is when it is evaluated. No real diffrence on the surface, all in the server.

Reactive_Var<-reactive({c(input$Test_R, input$Test_R2, input$Test_R3)})

output$React_Out<-renderTable({
Reactive_Var()
})

# Create an observe Evironment. Note that we cannot access the created "df" outside
# of the env. A, B,and C will update with any input into any of the three Text Feilds.
observe({
A<-input$Test
B<-input$Test2
C<-input$Test3
df<-c(A,B,C)
output$Observe_Out<-renderTable({df})
})

#We can change any input as much as we want, but the code wont run until the trigger
# input$Go is pressed.
observeEvent(input$Go, {
A<-input$Test_OE
B<-input$Test_OE2
C<-input$Test_OE3
df<-c(A,B,C)
output$Observe_Out_E<-renderTable({df})
})

}
shinyApp(ui, server)

reactive
Create a variable that can be changed over time by user inputs, evaluates "lazy" meaning only when called.

observe
Continually monitor reactive events and variables, whenever ANY reactive variable is changed in the environment (the observed environment), the code is evaluated. Can change values of previously defined reactive variables, cannot create/return variables.

observeEvent (Domino Effect)
Continually monitor ONE defined reactive variable/event (the trigger) and run the code when the the trigger is activated by change/input of that trigger. Can change values of previously defined reactive variables, cannot create/return variables.

eventReactive Create a variable, with a defined trigger similar to observeEvent. Use this when you want a reactive variable that evaluates due to a trigger instead of when it is called.

Feel free to edit to improve or correct this post.

Using a reactive value and a reactive expression to trigger an observeEvent

observeEvent has ignoreNULL = TRUE set by default. As you correctly suspect, while the observer will trigger for a change to any part of {x;y}, it will only do that if the value of that whole expression is not NULL. In this example, as long as y is NULL, the observer won't trigger, no matter what you do to x. This is easily fixed by setting ignoreNULL = FALSE.

When to use a reactive expression vs to use - to create a global object

At the outset, I think we need to clarify the scope of the question.

  1. If you write a function in R, it is good practice to return value using either return() at the end of the function, for clarity, or at least putting the data object last before the curly brackets. in this context, the use of <<- can create any sort of errors and complications (in short is a reminder of spaghetti programming, breaking the rule of having a single exit point to the function in question).
  2. Different is the case of shiny special functions. I just like to remind that shiny coders usually does not use return() at the end of a shiny function (even if it would still work). Here either there is no value returned, or reactive is used or other similar constructs.

Part of the confusion comes from the minimal example in the question: the example is not a shiny special function but just a normal function.

In shiny, using for example constructs like observe, it may happen to use <<- to update global objects. A very good example (a great piece of code I often go back to) is the application ShinyChat developed by Jeff Allen, one of shiny developers - see here on github.
In this case Jeff uses <<- to update a reactiveValue defined at the global level from within an observe function: very good style.

I conclude with a minor point: if you have any function and you update a variable with <<-, and you have a variable of the same name defined locally, only the global variable is updated.
In these cases you need to do something like

data <<- data <- 3

From right to left: the first <- updated the local variable named data: the second <<- updated the global variable named data. Talk about confusing.

On the other hand, in shiny or standard R I never noticed a drastic slowness because of <<-, certainly provided that there aren't too many in the code.

On the issue of alternative techniques like reactive or reactiveValues, please see this good answer on SO to a similar question here.

difference between global variable and reactive value

A reactiveValue is something that you can update from observers or observeEvents. Any reactive expression that is dependent on that value, will then get invalidated, and thus updated if necessary.

A global variable is a variable that is defined globally, i.e. all users that are in the process of your shiny App share that variable. The most simple example of this would be a large dataset.

Note that your 'global' variable is not global. You define it as:

global <- list()

within the server. Therefore, it is unique to one user in your app, it is not shared. i.e. if it would be

global <- runif(1)

the numbers in 'global' would be different for multiple users. If you want the value to be the same, you should initialize it above your server definition. Note also that this line:

global <- creerCube(input)

does not modify your 'global' variable, because it is out of scope. It creates a variable 'global' within your observer, and discards it when the function finishes. What is probably best is to set global as a reactiveValue:

global <- reactiveVal()

and then update it as:

 global(creerCube(input))

I hope this helps.

R Shiny update reactive value using a non reactive variable

The start value needs to be reactive as well to working with other reactive values. Here's a re-write that will work

library(shiny)

ui <- fluidPage(
uiOutput("text_header"),
actionButton("go", "Go")
)

server <- function(input, output) {
start <- reactiveVal(0)
rv <- reactive(paste("clicks",start()))
observeEvent(input$go, {
start(start() + 1)
})
output$text_header <- renderUI({
h1(rv(), align = "center")
})
}

shinyApp(ui, server)

We make start the reactive value, and then rv just depends completely on the current value of start. Maybe "start" isn't the best word here, you could rename it

library(shiny)

ui <- fluidPage(
uiOutput("text_header"),
actionButton("go", "Go")
)

server <- function(input, output) {
start <- 0
clickCount <- reactiveVal(start)
rv <- reactive(paste("clicks",clickCount()))
observeEvent(input$go, {
clickCount(clickCount() + 1)
})
output$text_header <- renderUI({
h1(rv(), align = "center")
})
}

shinyApp(ui, server)

How to manage/call a Shiny `reactive()` expression with unfulfilled `validate(need())` conditions

When using req or validate/need, the chain of reactivity is disrupted if not completely valid. This means that anything that depends on the reactive block will also not fire (due to the data block). I don't know of a way to "peek" into the reactive block's validation, since that would involve evaluating the expression and catching the shiny-specific signals.

Typically, it is sufficient in a shiny app that the actbtn observer will not fire due to data not validating. However, if you need to know in one expression whether another is fire-able, you can set up a third reactive block that always returns a known element (i.e., no validate in it).

library(shiny) # don't use require without checking its return value
# https://stackoverflow.com/a/51263513/3358272

ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),

mainPanel(
actionButton(inputId = "actbtn", label = "Print in console reactive value")
)
)
)

server <- function(input, output) {

isgood <- reactive({
if (is.null(input$data) || !nzchar(input$data)) {
"Please select a data set"
} else if (!input$data %in% c("mtcars", "faithful", "iris")) {
"Unrecognized data set"
} else character(0)
})

data <- reactive({
validate(need(!length(isgood()), isgood()))
get(input$data, 'package:datasets')
})

observeEvent(input$actbtn, {
validate(need(!length(isgood()), isgood()))
print("This message will only be printed when data() validate/need are fulfilled.")
})
}

shinyApp(ui, server)

An alternative is to disable the button (cannot be clicked) until data() is valid. This can be done with shinyjs, which also requires a change to the ui component.

library(shiny)
library(shinyjs)

ui <- fluidPage(
shinyjs::useShinyjs(), # <-- add this
sidebarLayout(
sidebarPanel(
selectInput("data", label = "Data set",
choices = c("", "mtcars", "faithful", "iris"))
),

mainPanel(
actionButton(inputId = "actbtn", label = "Print in console reactive value")
)
)
)

server <- function(input, output) {

observeEvent(TRUE, {
message("quux")
shinyjs::disable("actbtn")
}, once = TRUE)

data <- reactive({
validate(
need(input$data != "", "Please select a data set"),
need(input$data %in% c("mtcars", "faithful", "iris"),
"Unrecognized data set"),
need(input$data, "Input is an empty string"),
need(!is.null(input$data),
"Input is not an empty string, it is NULL")
)
get(input$data, 'package:datasets')
})

observeEvent(data(), {
shinyjs::toggleState("actbtn", condition = !is.null(data()))
})

observeEvent(input$actbtn, {
print("This message will only be printed when data() validate/need are fulfilled.")
})
}

shinyApp(ui, server)

Due to the choices you have available in your UI, the toggleState observer will only ever toggle the disabled state once, after that it should always be valid. Because of that, there are ways to possibly reduce it. For instance, once could add once=TRUE as in the first observeEvent I added, in which case once the observer fires once, it is removed and never fires again. In your real-world use, if you believe that data() might toggle back to an unusable state (e.g., 0 rows), then you would likely want to keep the default of once=FALSE and add more logic on nrow(data()) to your data validation.



Related Topics



Leave a reply



Submit