Reactivevalues vs reactive

To pytanie jest związane z tym . Oba mogą generować tę samą funkcjonalność, ale implementacja jest nieco inna. Jedną z istotnych różnic jest to, że reactiveValue jest kontenerem, który może mieć kilka wartości, np. input$. W shiny documentation funkcjonalność jest zwykle zaimplementowana przy użyciu reactive(), ale w większości przypadków uważam reactiveValues() za wygodniejszą. Jest tu jakiś haczyk? Czy są jakieś inne poważne różnice między tymi dwoma, których mogę nie być świadomy off? Czy te dwa fragmenty kodu są równoważne?

Zobacz ten sam przykładowy kod zaimplementowany przy użyciu:

  1. Wyrażenie reaktywne:

    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. Wartość reaktywna:

    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)
    
 74
Author: Community, 2016-09-11

2 answers

Jest pewien haczyk, choć w twoim przykładzie nie wejdzie on w grę.

The shiny Programiści zaprojektowali reactive() jako lazy , co oznacza, że zawarte w nim wyrażenie zostanie wykonane tylko wtedy, gdy zostanie wywołane przez jedną z jego zależności. Gdy jedna z reaktywnych zależności zostanie zmieniona, czyści pamięć podręczną i powiadamia o własnych zależnościach, ale sama nie jest wykonywana, dopóki nie poprosi o to jeden z tych zależności. (Więc jeśli, powiedzmy, jego jedynym zależnym elementem jest textOutput() element na zakładka Ukryta, nie zostanie wykonana, dopóki ta zakładka nie zostanie otwarta.)

observe(), z drugiej strony, jest eager; wyrażenie, które zawiera, zostanie wykonane natychmiast, gdy jedna z jego reaktywnych zależności zostanie zmieniona-nawet jeśli jego wartość nie jest potrzebna żadnemu z jego zależności (a w rzeczywistości nawet jeśli nie ma żadnych zależności). Taki zapał jest pożądany kiedy dzwonisz observe() po jego skutki uboczne, ale może być marnotrawny kiedy używasz go tylko do przekazywania zwracanej wartości jego zawartości innym reaktywnym wyrażeniom lub punktom końcowym w dół wiersza.

Joe Cheng dość dobrze wyjaśnia to rozróżnienie w swojej prezentacji Shiny Developer Conference 2016 na temat "Effective reactive programming", dostępnej tutaj. Zobacz zwłaszcza bit rozpoczynający się około 30: 20 w drugiej godzinie prezentacji . Jeśli oglądasz do 40: 42 (mrugnij, a przegapisz!) krótko scharakteryzował zachowanie observe()/reactiveValue () kombinacja, którą lubisz.

 88
Author: Josh O'Brien,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-20 15:10:34

Prawdą jest, że te dwie konstrukcje są podobne i że wiele razy można użyć jednej z nich do rozwiązania problemu. Ale zwykle bardziej sensowne jest używanie jednego lub drugiego.

W przypadku Fibonacciego, myślę, że użycie wyrażenia reactive() ma większy sens, ponieważ currentFib jest wartością, która powinna zostać zmodyfikowana w bardzo określonych przewidywalnych czasach(tj. jeżeli input$n zmienia się, należy odpowiednio zaktualizować wartość reaktywną lub zareagować na tę zmianę).

Ale w niektórych inne przypadki może być prostsze i lepsze użycie reactiveValues. Pokażę dwa przykłady.

Po pierwsze, ilekroć masz zmienną, o której myślisz, że ma jakiś stan (a nie tylko reaguje na aktualizowaną inną wartość), myślę, że użycie reactiveValues jest lepsze.

Przykład:

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)

W powyższym kodzie mamy zmienną total, która ma zmienny stan i jest znacznie bardziej intuicyjne, aby myśleć o niej jako o typowej zmiennej i używać jej jako takiej. To jest najbardziej częsty przypadek, gdy używam reactiveValues.

I również używać reactiveValues gdy zmienna może być aktualizowana w wielu miejscach. Aby skorzystać z przykładu Fibonacciego, rozważ następującą aplikację shiny, gdzie liczba n może być ustawiona przez jedno z dwóch wejść:

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)

Ten przykład może wydawać się nieco dziwny w kontekście Fibonacciego, ale mam nadzieję, że zobaczysz, jak w niektórych innych złożonych aplikacjach, czasami możesz chcieć ustawić wartość zmiennej w różnych miejscach i może to być więcej intuicyjnie można to zrobić za pomocą reactiveValue, a nie wyrażenia reaktywnego, które musi być zaimplementowane w jednym bloku.

Mam nadzieję, że to było pomocne i ma sens. Oczywiście jest to tylko moje osobiste podejście do tematu, niekoniecznie jest to to, co zamierzali twórcy shiny, ale w ten sposób nauczyłem się używać tych dwóch metod.

 53
Author: DeanAttali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-08 16:20:05