web-dev-qa-db-fra.com

Avantages de réactif vs observer vs observerEvent

J'ai tout lu sur la programmation réactive brillante. Je suis un peu confus. Les éléments suivants fonctionnent tous, mais quelle est la méthode préférée et pourquoi? Évidemment, l'exemple ci-dessous est simple, mais vais-je rencontrer des problèmes lors de la création d'une application plus grande avec l'une des méthodes?

J'ai eu tendance à privilégier le style du code serveur n ° 1. Raison d'être, est que je suis en mesure de briser les déclarations si. Cela me semble beaucoup plus lisible. Encore une fois, les exemples simples ci-dessous ne sont pas terriblement complexes, mais vous pouvez facilement imaginer comment les codes de serveur 2 et 3 pourraient devenir très déroutants avec de nombreuses instructions imbriquées if/if.

Code d'interface utilisateur

library(shiny)

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

  textOutput('result')

)

Code Serveur 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)

Code Serveur 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)

Code Serveur

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)
26
agf1997

Tout d’abord, ce genre de choses est un peu ambigu, et pas très intuitif à certains égards, il est même dit sur le blog Shiny!

Voici ma meilleure compréhension du sujet ..

Commençons par reactive

La fonction réactive permet à un utilisateur de surveiller le statut d'une entrée ou d'une autre variable changeante et de renvoyer la valeur à utiliser ailleurs dans le code. La surveillance d’une variable réactive est considérée comme paresseuse, "Les expressions réactives utilisent une évaluation paresseuse; c’est-à-dire que, lorsque leurs dépendances changent, elles ne se ré-exécutent pas tout de suite, mais attendent plutôt qu’elles soient appelées par une autre personne. ( Source) ". Vous le montrez bien dans l'exemple 2, car vous pouvez appeler la variable dans l'environnement renderText, une fois appelé le code dans l'appel réactif s'exécute et réévalue la variable.

Pour les nerfs de la science, cela ressemble beaucoup à la mécanique quantique dans le sens où appeler la variable réactive (l'observer) la fait changer en la réévaluant, trop longtemps.

Maintenant à observe

Observe est semblable au réactif, la principale différence est qu’il ne renvoie aucune valeur à un autre environnement que le sien et qu’il n’est pas paresseux. La fonction observe surveille en permanence les modifications de toutes les valeurs réactives dans son environnement ​​et exécute le code dans son environnement lorsque ces valeurs sont modifiées. Donc, observer n’est pas une évaluation "paresseuse", car il n’attend pas d’être appelé pour être réévalué. Notez à nouveau que vous ne pouvez pas affecter de variables à observer.

Par souci d'expérience:

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)

enter image description here

Il est important de noter que pendant le code exécuté dans observe, nous pouvons manipuler des variables réactives de l’environnement extérieur. Dans votre cas, vous attribuez text <- reactiveValues() puis vous manipulez cela en appelant text$result <- 'Hi there'. Nous pouvons également faire des choses comme update selectInput choice ou d'autres widgets brillants, mais nous ne pouvons pas affecter de variables non réactives dans cet environnement comme notre getStatus dans l'exemple ci-dessus. Et cette idée est mentionnée dans la documentation observe,

"Un observateur est comme une expression réactive en ce sens qu'il peut lire des valeurs réactives et appeler des expressions réactives. Il s'exécute automatiquement lorsque ces dépendances changent. Mais contrairement aux expressions réactives, il ne produit pas de résultat et ne peut pas être utilisé comme une entrée dans d'autres expressions réactives. Ainsi, les observateurs ne sont utiles que pour leurs effets secondaires (par exemple, effectuer des E/S) ( Source ) "

Enfin, observeEvent

La meilleure façon d'utiliser observeEvent consiste à le considérer comme un déclencheur défini, car il surveille un événement ou une modification dans une variable, puis se déclenche lorsque l'événement se produit. J'utilise le plus souvent ceci pour regarder l'entrée sur les boutons, car c'est un événement défini dans lequel je veux que des choses se produisent après que le bouton est enfoncé. Il utilise un environnement isolate qui, à mon avis, convient parfaitement au fonctionnement de cette fonction.

Dans cet environnement, nous pouvons appeler une série de variables réactives, mais nous n'en définissons qu'une comme déclencheur. La principale différence entre observeEvent et observe est le déclencheur, car observe est exécuté à tout moment, et observeEvent attend le déclencheur. Notez que cet environnement est similaire à observer en ce sens qu'il ne renvoie pas de variables non réactives.

Résumé

Voici un exemple qui rassemble toutes ces idées:

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)

reactiveCréer une variable pouvant être modifiée au fil du temps par les entrées utilisateur, évalue le mot "paresseux" uniquement lors de son appel.

observe Surveille en permanence les événements et les variables réactifs, chaque fois que N’IMPORTE LAQUELLE la variable réactive est modifiée dans l’environnement (l’environnement observé), le code est évalué. Peut changer les valeurs des variables réactives définies précédemment, impossible de créer/retourner des variables.

observeEvent (Effet Domino) Surveille en permanence UN variable/événement réactif défini (le déclencheur) et lance le code lorsque le déclencheur est activé par changement/entrée de ce déclencheur. Peut changer les valeurs des variables réactives définies précédemment, impossible de créer/retourner des variables.

eventReactiveCréer une variable, avec un déclencheur défini similaire à observeEvent. Utilisez cette option lorsque vous souhaitez une variable réactive dont l’évaluation est due à un déclencheur et non à son appel.

J'espère que cela aide, et si je me trompe dans ma compréhension ou s'il pourrait y avoir plus de précisions, n'hésitez pas à modifier cette réponse.

26
Chabo

Il existe déjà une réponse très détaillée, je vais donc ajouter mon propre petit bref:

Autant que possible, restez sur reactive() plutôt que sur reactiveValues(). Normal reactive() fonctionne plus en ligne avec la philosophie de programmation réactive, ce qui signifie que l'expression reactive() indique simplement brillant comment ​​la variable est calculée , sans avoir à spécifier when. Shiny se chargera de déterminer quand le calculer. Ils seront évalués paresseusement (uniquement lorsque cela sera nécessaire), ils cacheront leur valeur, ils travailleront avec la fonctionnalité de mise en favori - c'est simplement la manière dont brillant a été conçu et qui devrait toujours être le premier choix.

Avec reactiveValues(), vous êtes maintenant de retour dans plus programmation impérative, pas réactif. Il existe des cas où reactive() ne le coupe pas et que vous devez utiliser reactiveValues() (ou reactiveVal()), mais ils ne doivent être utilisés que si reactive() ne fonctionnera pas. Par exemple, avec reactive(), la variable est définie à un seul endroit. Par conséquent, si vous souhaitez définir la variable à plusieurs endroits, vous devez utiliser reactiveValues(). Pour une explication plus complète de la différence entre reactive() et reactiveValues(), vous pouvez voir ma réponse d'un ancien message

observe() vs observeEvent(): vous pouvez les considérer comme la même chose, mais observeEvent() est simplement un raccourci pour observe() qui est déclenché par certaines variables, et le reste du code est isolate()- ed. En fait, tout ce que vous faites avec observeEvent() peut toujours être fait avec observe() aussi, c'est deux types de la même chose.

8
DeanAttali