Преимущества реактивного перед наблюдением перед наблюдением за событием

Я читал все, что мог, о блестящем реактивном программировании. Я немного запутался. Все перечисленное ниже работает, но какой метод предпочтительнее и почему? Очевидно, что приведенный ниже пример прост, но возникнут ли у меня проблемы при создании более крупного приложения с помощью любого из методов?

Я склонен тяготеть к стилю в серверном коде №1. Причина в том, что я могу разбить операторы if. Мне это кажется более читаемым. Опять же, приведенные ниже простые примеры не слишком сложны, но вы легко можете представить, как серверный код 2 и серверный код 3 могут сильно сбивать с толку из-за большого количества вложенных операторов if / if else.

Код пользовательского интерфейса

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)

Хороший вопрос и отличные воспроизводимые примеры. Если вы еще не видели его, возможно, вы захотите взглянуть на этот связанный вопрос и два его ответа.

Josh O'Brien 26.10.2018 23:30

И чем isolate({...}) отличается от всего этого? Он НЕ реагирует на переменные, указанные внутри, выполняется при достижении, но все равно считается реактивной средой, верно?

Josiah Yoder 24.06.2019 19:54

Просто примечание: на самом деле вы МОЖЕТЕ отслеживать более одной переменной в ObservationEvent, например: observeEvent(c(input$a,input$b),{code})

Ricardo Fernandes Campos 18.08.2021 19:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
77
3
24 488
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Во-первых, этот материал неоднозначен и в некотором смысле не очень интуитивен, об этом даже говорится в блоге Shiny!

Вот мое лучшее понимание темы ..

Начнем с reactive

Реактивная функция позволяет пользователю отслеживать состояние ввода или другой изменяющейся переменной и возвращать значение, которое будет использоваться в другом месте кода. Мониторинг реактивной переменной считается ленивым, «Реактивные выражения используют ленивую оценку; то есть, когда их зависимости меняются, они не выполняются повторно сразу, а скорее ждут, пока их не вызовет кто-то другой. (Источник)».. Вы хорошо это продемонстрировали в примере 2, поскольку вы можете вызвать переменную внутри среды renderText, после того, как вызываемый код внутри реактивного вызова выполняет и повторно оценивает переменную.

Для ботаников это очень похоже на квантовую механику в том, что вызов реактивной переменной (наблюдение за ней) заставляет ее измениться путем переоценки, слишком большой натяжкой?

Теперь о 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)

Важно отметить, что во время выполнения кода в observe мы можем манипулировать реактивными переменными внешней среды. В вашем случае вы назначаете text <- reactiveValues(), а затем управляете им, вызывая text$result <- 'Hi there'. Мы также можем делать такие вещи, как обновлять варианты selectInput или другие блестящие виджеты, но мы не можем назначать никакие нереактивные переменные в этой среде, такие как наш getStatus в приведенном выше примере. И эта идея упоминается в документации observe,

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

Наконец, observeEvent

Лучший способ использовать observeEvent - рассматривать его как определенный триггер, поскольку он наблюдает за одним событием или изменением переменной, а затем срабатывает, когда событие происходит. Чаще всего я использую это для просмотра ввода кнопок, поскольку это определенное событие, в котором я хочу, чтобы что-то произошло после нажатия кнопки. Он использует среду isolate, которая, на мой взгляд, является идеальным названием для того, как работает эта функция.

Внутри этой среды мы можем вызывать кучу реактивных переменных, но мы определяем только одну как триггер. Основное различие между observeEvent и observe заключается в триггере, так как 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 Постоянно отслеживайте реактивные события и переменные, всякий раз, когда реактивная переменная ЛЮБОЙ изменяется в среде (наблюдаемой среде), выполняется оценка кода. Может изменять значения ранее определенных реактивных переменных, не может создавать / возвращать переменные.

observeEvent (эффект домино) Постоянно контролировать определенную реактивную переменную / событие ОДИН (триггер) и запускать код, когда триггер активируется изменением / вводом этого триггера. Может изменять значения ранее определенных реактивных переменных, не может создавать / возвращать переменные.

eventReactiveСоздать переменную, с определенным триггером, аналогичным observeEvent. Используйте это, если вам нужна реактивная переменная, которая оценивается по триггеру, а не по его вызову.

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

И чем изолят отличается от всего этого? Isolate обычно используется в другой функции, но я только что видел пример, где это не так: github.com/rstudio/shiny/issues/141#issuecomment-351857670

Josiah Yoder 24.06.2019 19:47
isolate({...}) НЕ реагирует на переменные, указанные внутри, выполняется при достижении, но все равно считается реактивной средой, верно?
Josiah Yoder 24.06.2019 19:57

Уже есть очень подробный ответ, поэтому я просто добавлю свои короткие простые два цента:

По возможности придерживайтесь reactive(), а не reactiveValues(). Обычный reactive() работает в большей степени с философией shiny реактивное программирование, что означает, что выражение reactive() просто сообщает shiny как, что переменная вычислена, без необходимости указывать когда. Shiny позаботится о том, чтобы определить, когда его рассчитывать. Они будут лениво оцениваться (только когда это необходимо), они будут кэшировать свое значение, они будут работать с функцией закладок - это просто способ, которым блестящий был разработан, и всегда должен быть первым выбором.

С reactiveValues() вы теперь снова находитесь на территории императивное программирование, а не реагируете. Бывают случаи, когда reactive() не режет его, и вам нужно использовать reactiveValues() (или reactiveVal()), но их следует использовать только в том случае, если reactive() не работает. Например, в reactive() существует только одно место, где определена переменная, поэтому, если вы хотите определить переменную в нескольких местах, вам нужно будет использовать reactiveValues(). Для более полного объяснения разницы между reactive() и reactiveValues() вы можете увидеть мой ответ из старого поста

observe() против observeEvent(): вы можете думать о них как об одном и том же, но observeEvent() - это просто ярлык для observe(), который запускается определенными переменными, а остальная часть кода - это isolate()-ed. Фактически, все, что вы делаете с observeEvent(), всегда можно сделать и с observe(), это две разновидности одного и того же.

Другие вопросы по теме