反应式与观察式与observeEvent的优势

时间:2018-10-26 21:16:09

标签: r shiny

我已经阅读了有关闪亮的反应式编程的所有内容。我有点困惑。以下所有方法都可以,但是首选方法是什么?为什么?显然,以下示例很简单,但是使用任何一种方法创建更大的应用程序时,我都会遇到麻烦吗?

我一直倾向于使用服务器代码#1中的样式。原因是,我能够分解if语句。对我来说,这似乎更具可读性。同样,下面的简单示例并不是很复杂,但是您可以轻松地想象到服务器代码2和服务器代码3会因大量嵌套if / if else语句而变得非常混乱。

UI代码

library(shiny)

ui <- fluidPage(
  selectInput(inputId = 'choice',
              label = 'Choice',
              choice = c('Hello','Goodbye'),
              selected = c('Hello')
  ),

  textOutput('result')

)

服务器代码1

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

  text <- reactiveValues()

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

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

  output$result <- renderText({
    text$result
  })

})

shinyApp(ui = ui, server = server)

服务器代码2

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

  getStatus <- reactive({

    if (input$choice == 'Hello') {
      'Hi there'
    } else if (input$choice == 'Goodbye'){
      'See you later'
    }
  })

  output$result <- renderText({
    getStatus()
  })

})

shinyApp(ui = ui, server = server)

服务器代码3

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

  text <- reactiveValues()

  observeEvent(input$choice,{
    if (input$choice == 'Hello') {
      text$result <- 'Hi there'
    } else if (input$choice == 'Goodbye') {
      text$result <- 'See you later'
    }
  })

  output$result <- renderText({
    text$result
  })

})

shinyApp(ui = ui, server = server)

2 个答案:

答案 0 :(得分:17)

首先,这些东西含糊不清,并且在某些方面不是很直观,甚至在Shiny博客上也是如此!

这是我对该主题的最佳理解。

让我们以reactive

开头

反应式功能允许用户监视输入或其他更改变量的状态,并返回要在代码中其他位置使用的值。对反应变量的监视被认为是惰性的,“反应性表达式使用了惰性计算;也就是说,当它们的依赖关系发生变化时,它们不会立即重新执行,而是等到其他人调用它们。({ {3}}”。您将在示例2中很好地展示了这一点,因为您可以在renderText环境中调用变量,一旦在响应调用中调用了代码,就会执行并重新评估该变量。 / p>

对于科学呆子来说,这很像量子力学,因为调用反应变量(观察它)会导致它通过重新评估而改变,从而导致其变化很大?

立即访问observe

Observe与之类似,它的主要区别是它除了自身之外,不将任何值返回给其他任何环境,并且它不是惰性的。观察功能持续监视其环境中所有无功值的任何变化,并在这些值更改后在其环境中运行代码。因此,observe不是“惰性”评估,因为它不会在重新评估之前等待被调用。再次注意,您不能通过观察分配变量。

为了实验:

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)

Source)

需要注意的重要一点是,在observe中执行的代码期间,我们可以操纵外部环境反应变量。在您的情况下,您分配text <- reactiveValues(),然后通过调用text$result <- 'Hi there'进行操作。我们还可以执行更新selectInput选项或其他闪亮的小部件之类的操作,但是我们不能在这种环境中分配任何非反应性变量,例如上面示例中的getStatus。 observe文档

中提到了这个想法

“观察者就像一个反应表达式,它可以读取反应值并调用反应表达式,并且当这些依赖关系发生变化时将自动重新执行。但是与反应表达式不同,它不会产生结果并且不能用作其他反应式表达式的输入。因此,观察者仅对它们的副作用有用(例如,执行I / O)(enter image description here)“

最后,observeEvent

使用observeEvent的最佳方法是将其视为已定义的触发器,因为它监视一个事件或变量的更改,然后在事件发生时触发。我最常使用它来监视按钮的输入,因为这是一个已定义的事件,在该事件中,我希望按钮被按下后才能发生。它使用isolate环境,我认为这是此函数如何工作的完美名称。

在此环境中,我们可以调用一堆反应变量,但是我们仅将一个定义为触发器。 observeEventobserve之间的主要区别是触发器,因为observe会在任何更改发生时运行,并且observeEvent等待触发器。请注意,此环境类似于观察到的环境,因为它不返回非反应变量。

摘要

以下是将所有这些想法结合在一起的示例:

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 创建一个变量,该变量可以随用户输入随时间变化,仅在调用时评估“惰性”含义。

observe 持续监视反应性事件和变量,只要在环境(观察到的环境)中更改了 ANY 反应性变量,就会对代码进行评估。可以更改先前定义的反应变量的值,不能创建/返回变量。

observeEvent(多米诺骨牌效应) 持续监视一个定义的反应变量/事件(触发器),并在通过触发器的更改/输入激活触发器时运行代码。可以更改先前定义的反应变量的值,不能创建/返回变量。

eventReactive 创建变量,其定义的触发器类似于observeEvent。当您想要一个因触发器而求值的反应性变量而不是在调用时使用此变量。

我希望这会有所帮助,如果我误会了我的理解或者可能需要进一步澄清,请随时编辑此答案。

答案 1 :(得分:7)

已经有一个非常详细的答案,所以我只添加自己的简短两分钱:

请尽可能坚持使用reactive()而不是reactiveValues()。正常的reactive()与Shiny的反应式编程原理更加内联,这意味着reactive()表达式仅告诉Shiny 变量的计算方式,而无需指定何时。 Shiny将负责确定何时进行计算。他们将被懒惰地评估(仅在需要时),将缓存其值,将其与书签功能配合使用-这只是设计光泽的方式,应始终是首选。

有了reactiveValues(),您现在又回到了更多的命令式编程领域,不再是被动的。在某些情况下,reactive()没有削减,您需要使用reactiveValues()(或reactiveVal()),但是只有在reactive()不起作用的情况下才应使用它们。例如,对于reactive(),只有一个地方定义了变量,因此,如果要在多个地方定义变量,则需要使用reactiveValues()有关reactive()reactiveValues()之间差异的更完整说明,您可以查看my answer from an old post

observe()observeEvent():您可以将它们视为同一事物,但是observeEvent()只是observe()的快捷方式,它是由某些变量触发的,并且其余代码是isolate()版。实际上,您使用observeEvent()所做的任何事情也始终可以使用observe()完成,这是同一件事的两种味道。