Подтвердить что ты не робот

R Shiny: реактивные эффекты против реактивных

Этот вопрос связан с этим. Эти два могут генерировать те же функции, но реализация немного отличается. Одно существенное отличие состоит в том, что reactiveValue представляет собой контейнер, который может иметь несколько значений, например input$. В блестящая документация функциональность обычно реализуется с помощью reactive(), но в большинстве случаев я нахожу reactiveValues() более удобным. Есть ли здесь улов? Существуют ли какие-либо другие существенные различия между этими двумя, о которых я, возможно, не знаю? Являются ли эти два фрагмента кода эквивалентными?

См. тот же примерный код, реализованный с помощью:

  • реактивное выражение:

    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)
    
  • реактивное значение:

    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)
    
4b9b3361

Ответ 1

Есть уловка, хотя в вашем примере это не будет играть.

Разработчики блестящие разработали reactive() как ленивый, что означает, что содержащееся в нем выражение будет выполняться только тогда, когда оно вызывается одним из его иждивенцев. Когда одна из его реактивных зависимостей изменяется, она очищает свой кеш и уведомляет о своих иждивенцах, но сама она не выполняется до тех пор, пока ее не попросит один из этих иждивенцев. (Так что, если, скажем, его единственным зависимым является элемент textOutput() на скрытой вкладке, он фактически не будет выполняться до тех пор, пока эта вкладка не будет открыта.)

observe(), с другой стороны, нетерпеливо; выражение, которое оно содержит, будет выполняться сразу же, когда изменяется одна из его реактивных зависимостей - даже если это значение не требуется ни одному из его иждивенцев (а на самом деле даже если у него нет иждивенцев). Такое желание желательно, когда вы вызываете observe() для его побочных эффектов, но может быть расточительным, когда вы используете его только для передачи возвращаемого значения его содержимого другим реактивным выражениям или конечным точкам по строке.

Джо Ченг хорошо объясняет это различие в своей презентационной конференции "Блестящая презентация" в 2016 году "Эффективное реактивное программирование", доступном здесь. См., В частности, бит, начинающийся около 30:20 во время презентации во второй час. Если вы смотрите до 40:42 (мигайте, и вы это пропустите!), Он кратко характеризует поведение комбинации observe()/reactiveValue (), которая вам нравится.

Ответ 2

Верно, что две конструкции подобны, и много раз вы можете использовать один из них для решения своей проблемы. Но обычно имеет смысл использовать один или другой.

В случае с фибоначчи я считаю, что использование выражения 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)

Этот пример может показаться немного странным в контексте фибоначчи, но, надеюсь, вы можете увидеть, как в некоторых других сложных приложениях иногда вы можете установить значение переменной в разных местах, и это может быть более интуитивно понятным он использует реактивное значение, а не реактивное выражение, которое должно быть реализовано в одном блоке.

Надеюсь, это было полезно и имеет смысл. Конечно, это только мое личное занятие по этому вопросу, это не обязательно то, что планировали блестящие разработчики, но именно так я научился использовать эти два метода.