96 votes

R Shiny: réactifValues vs réactif

Cette question est liée à cette une. Les deux peuvent générer les mêmes fonctionnalités, mais la mise en œuvre est légèrement différente. Une différence importante est que l' reactiveValue est un conteneur qui peut avoir plusieurs valeurs, comme input$. Dans le brillant de la documentation fonctionnalité est généralement mis en oeuvre à l'aide de reactive(), mais dans la plupart des cas, je trouve reactiveValues() plus pratique. Est-il un piège ici? Existe-il d'autres différences majeures entre les deux que je ne sois pas au courant? Ce sont ces deux extraits de code équivalent?

Voir le même exemple de code mis en œuvre à l'aide de:

  1. un réactif de l'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. un réactif de valeur:

    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)
    

114voto

Josh O'Brien Points 68397

Il y a un piège, mais il n'entrera pas en jeu dans votre exemple.

Le brillant développeurs conçu reactive() à être paresseux, ce qui signifie que l'expression contenue en elle ne sera exécutée que lorsqu'elle est appelée par l'une de ses personnes à charge. Lorsque l'un de ses réactif dépendances est transformée, elle efface sa mémoire cache et notifie sa propre charge, mais il n'est pas lui-même exécuté jusqu'à ce que demandé par l'une de ces personnes à charge. (Si, par exemple, sa seule personne est un textOutput() élément caché onglet, il ne sera pas effectivement être exécuté à moins que et jusqu'à ce que l'onglet est ouvert.)

observe(), d'autre part, est avide; l'expression qu'il contient sera exécutée immédiatement à chaque fois que l'un de ses réactif dépendances est changé-même si c'est la valeur n'est pas nécessaire, par un de ses personnes à charge (et, en fait, même si a pas de personnes à charge). Un tel désir est souhaitable lorsque vous appelez observe() pour ses effets secondaires, mais il peut être un gaspillage lorsque vous êtes seul à utiliser pour transmettre la valeur de retour de son contenu à d'autres réactifs expressions ou des points de terminaison en bas de la ligne.

Joe Cheng explique cette distinction très bien dans son 2016 Brillant Développeur de présentation de la Conférence sur "Efficace réactif de programmation", disponible ici. Voir en particulier les bits à partir d'environ 30:20 dans la présentation de la deuxième heure. Si vous regardez jusqu'à 40:42 (clignez des yeux et vous allez manquer!) brièvement, il caractérise le comportement de l' observe()/reactiveValue () combinaison que vous le souhaitez.

73voto

Dean Attali Points 12706

Il est vrai que les deux concepts sont similaires, et que plusieurs fois vous pouvez utiliser pour résoudre votre problème. Mais habituellement, il est plus logique d'utiliser l'un ou l'autre.

Dans le fibonacci le cas, je pense que l'utilisation d'un reactive() expression a plus de sens, car currentFib est une valeur qui devrait être modifié en très spécifiques à des moments prévisibles (ie. lors de l' input$n changements, le réactif de valeur doit être mis à jour en conséquence, ou de réagir à ce changement).

Mais dans certains autres cas, il peut être plus simple et mieux utiliser reactiveValues. Je vais vous montrer deux exemples.

Tout d'abord, chaque fois que vous avez une variable que vous considérez comme ayant une sorte d'état (plutôt que de simplement réagir à une valeur différente cours de mise à jour), je pense que l'utilisation d' reactiveValues est mieux.

Exemple:

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)

Dans le code ci-dessus, nous avons un total variable mutable état, et il est beaucoup plus intuitif de penser que c'est typique de la variable et de l'utiliser comme tel. C'est le cas le plus fréquent lorsque j'utilise reactiveValues.

J'ai aussi utiliser reactiveValues lorsqu'une variable peut être mis à jour en plusieurs endroits. Pour emprunter à partir de fibonacci exemple, considérez les points suivants brillant app, où l' n nombre peut être défini par l'une des deux entrées:

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)

Cet exemple peut sembler un peu étrange dans le contexte de fibonacci, mais j'espère que vous pouvez voir comment dans certains autres applications complexes, parfois, vous pouvez définir la valeur d'une variable dans des endroits différents et il peut être plus intuitive pour le faire à l'aide d'un reactiveValue plutôt que réactif expression qui doit être mis en œuvre dans un seul bloc.

J'espère que cela a été utile et a du sens. Bien sûr, ce n'est que mon point de vue personnel sur le sujet, ce n'est pas nécessairement ce que les brillants développeurs prévu, mais c'est de cette façon que j'ai appris à utiliser les deux méthodes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X