R Shiny:reactiveValues vs reactive

时间:2016-09-11 13:43:13

标签: r shiny

此问题与this one有关。这两者可以生成相同的功能,但实现略有不同。一个显着的区别是def myfunct (n: Int, steps: Int) = n match { case 1 => steps case even if n % 2 == 0 => // stuff case odd => // other stuff 是一个可以有多个值的容器,例如reactiveValue。在shiny documentation功能通常使用input$实现,但在大多数情况下,我发现reactive()更方便。这里有捕获吗?我可能没有注意到这两者之间是否存在其他重大差异?这两个代码片段是否相同?

查看使用以下内容实施的example code

  1. 反应性表达:

    reactiveValues()
  2. 反应值:

    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 个答案:

答案 0 :(得分:56)

有一个问题,虽然它在你的例子中不会发挥作用。

闪亮开发人员设计reactive() lazy ,这意味着其中包含的表达式只会在其中一个受属者调用时执行。当其中一个反应依赖项发生更改时,它会清除其缓存并通知其自己的依赖项,但在由其中一个依赖项要求之前,它本身不会执行。 (因此,如果,例如,它唯一的依赖项是隐藏选项卡上的textOutput()元素,除非并且直到该选项卡打开,否则它将无法实际执行。)

另一方面,

observe() 渴望;它包含的表达式将立即执行,只要其中一个反应依赖项发生更改 - 即使它的任何依赖项不需要它的值(事实上即使具有)没有家属)。当您因为副作用而调用observe()时,这种渴望是可取的,但当您仅使用它时,它可能浪费将其内容的返回值传递给其他反应式表达式或端点。

Joe Cheng在他的2016年Shiny开发者大会演讲中对#34;有效反应式编程&#34;,available here进行了很好的解释。特别是在演讲的第二个小时30:20左右开始。如果你一直观看到40:42(眨眼,你就会错过它!),他会简要描述你喜欢的observe() / reactiveValue ()组合的行为。

答案 1 :(得分:35)

这两个结构确实相似,而且很多时候你可以用任何一个来解决你的问题。但使用其中一种通常更有意义。

在斐波纳契案例中,我认为使用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要从fibonacci示例中借用,请考虑以下闪亮应用,其中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)

这个例子在fibonacci的上下文中看起来有点奇怪,但希望你能看到在其他一些复杂的应用程序中,有时你可能希望在不同的地方设置一个变量的值,它可以更直观地做它使用reactiveValue而不是必须在一个块中实现的反应式表达式。

希望这有用并且有意义。当然这只是我对这个主题的个人看法,它不一定是闪亮的开发者想要的,但这就是我学会使用这两种方法的方法。