R Shiny:reactiveValues与reactive的区别

96
这个问题与这个有关,它们可以生成相同的功能,但实现略有不同。一个显著的区别是reactiveValue是一个容器,可以有多个值,像input$一样。在shiny文档中,通常使用reactive()来实现功能,但在大多数情况下,我发现reactiveValues()更方便。这里有什么要注意的吗?这两者之间还有其他重大差异我可能不知道的吗?这两个代码片段是否等效?
请查看使用以下方式实施的示例代码
  1. a reactive expression:

    library(shiny)
    
    ui <- fluidPage( 
      shiny::numericInput(inputId = 'n',label = 'n',value = 2),
      shiny::textOutput('nthValue'),
      shiny::textOutput('nthValueInv')   
    )
    
    fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
    
    server<-shinyServer(function(input, output, session) {   
      currentFib         <- reactive({ fib(as.numeric(input$n)) })  
      output$nthValue    <- renderText({ currentFib() })
      output$nthValueInv <- renderText({ 1 / currentFib() })   
    })
    
    shinyApp(ui = ui, server = server)
    
  2. a reactive value:

    library(shiny)
    
    ui <- fluidPage( 
      shiny::numericInput(inputId = 'n',label = 'n',value = 2),
      shiny::textOutput('nthValue'),
      shiny::textOutput('nthValueInv')  
    )
    
    fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
    
    server<-shinyServer(function(input, output, session) { 
      myReactives <- reactiveValues()  
      observe(  myReactives$currentFib <-  fib(as.numeric(input$n))  ) 
      output$nthValue    <- renderText({ myReactives$currentFib  })
      output$nthValueInv <- renderText({ 1 / myReactives$currentFib  }) 
    })
    
    shinyApp(ui = ui, server = server)
    
2个回答

114

但需要注意的是,在你的示例中这个问题不会出现。

shiny开发者设计了reactive()函数具有延迟执行的特性,也就是说包含在其中的表达式只有在被其依赖项之一调用时才会被执行。当它的一个反应性依赖项被更改时,它会清除其缓存并通知自己的依赖项,但它本身直到被这些依赖项之一要求时才会被执行。(所以,如果说它唯一的依赖项是一个隐藏选项卡上的textOutput()元素,只有该选项卡被打开时它才真正被执行。)

observe()则恰恰相反,它是急切执行的;它包含的表达式将在其反应性依赖项之一更改时立即执行——即使其值不需要传递给任何依赖项(事实上即使没有任何依赖项)。当您仅使用observe()将其内容的返回值传递给其他反应性表达式或端点时,这种急切性可能是浪费的,但在您为其副作用调用observe()时,它是可取的

Joe Cheng在他2016年关于“有效反应式编程”的Shiny开发者会议演讲中很好地解释了这种区别,可以在此处找到。特别是观看演讲的第二个小时约30:20开始部分。如果一直观看到40:42(眨眼就错过了!),他会简要描述您喜欢的 observe()/reactiveValue()组合的行为。


2
感谢您的见解。我想带回家的信息是,使用observe/reactiveValue可能不太高效。但是,正如daattali所指出的那样,有些情况下,reactiveValues更容易理解/实现。这应该在shiny文档中得到更好的解释。 - Eduardo Bergel

73

确实,这两个结构非常相似,通常可以使用其中一个来解决您的问题。但是通常更有意义使用其中之一

在斐波那契案例中,我认为使用reactive()表达式更有意义,因为currentFib是一个应该在非常具体和可预测的时间内进行修改的值(例如,当input$n发生变化时,反应性值应相应更新,或者对该变化做出反应)。

但在其他情况下,使用reactiveValues可能会更简单、更好。我将展示两个例子。

首先,每当您有一个变量被视为具有某种状态而不仅仅是对不同值的反应时,我认为使用reactiveValues会更好

例子:

library(shiny)

ui <- fluidPage(
  "Total:",
  textOutput("total", inline = TRUE),
  actionButton("add1", "Add 1"),
  actionButton("add5", "Add 5")
)

server <- function(input, output, session) {
  values <- reactiveValues(total = 0)

  observeEvent(input$add1, {
    values$total <- values$total + 1
  })
  observeEvent(input$add5, {
    values$total <- values$total + 5
  })
  output$total <- renderText({
    values$total
  })
}

shinyApp(ui = ui, server = server)
在上述代码中,我们有一个具有可变状态的total变量,更直观的想法是将其视为典型变量并按照此方式使用。当我使用reactiveValues时,这是最常见的情况。
当一个变量可以在多个地方更新时,我也会使用reactiveValues。借鉴斐波那契数列的例子,考虑下面的闪亮应用程序,其中n数字可以通过两个输入之一进行设置:
library(shiny)

fib <- function(n) ifelse(n < 3, 1, fib(n - 1) + fib(n - 2))

ui <- fluidPage(
  selectInput("nselect", "Choose a pre-defined number", 1:10),
  numericInput("nfree", "Or type any number", 1),
  "Fib number:",
  textOutput("nthval", inline = TRUE)
)

server <- function(input, output, session) {
  values <- reactiveValues(n = 1)

  observeEvent(input$nselect, {
    values$n <- input$nselect
  })
  observeEvent(input$nfree, {
    values$n <- input$nfree
  })
  output$nthval <- renderText({
    fib(as.integer(values$n))
  })
}

shinyApp(ui = ui, server = server)

在斐波那契数列的背景下,这个例子可能看起来有点奇怪,但是希望您能看到,在其他一些复杂的应用程序中,有时您可能希望在不同的地方设置变量的值,并且使用reactiveValue比必须在一个块中实现的reactive expression更直观。

希望这对您有所帮助并且讲得通。当然,这只是我个人对这个主题的看法,不一定是shiny开发人员的意图,但这就是我学习使用这两种方法的方式。


我正在尝试在我正在构建的实时应用程序中使用reactivePoll。理想情况下,从reactivePoll提取的数据将是一个reactiveValues对象,但我认为它被视为reactive。有没有办法可以隔离依赖于reactivePoll的输出更新?例如,一个表格在x$var1上更新,另一个表格在x$var2上更新。每当x改变时,两个表格都会更新。由于x是一个reactive对象,通常我只需要使用isolate(x)$var1就可以完成任务。然而,对于reactives来说不是这样的。 - road_to_quantdom
这个答案帮助我解决了一个问题,该问题在这个SO问题中有解释:https://dev59.com/EdP7oIgBc1ULPQZF1sP7 - rove
这个答案帮助我解决了一个问题,问题详情在这个Stack Overflow的问题里:https://stackoverflow.com/q/75386611/9474704 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接