Понимание renderUI в Shiny

У меня есть приложение Shiny, которое позволяет пользователю загружать CSV-файл, а затем отображать данные. Затем у меня есть несколько пользовательских функций для создания различных элементов пользовательского интерфейса, которые передаются на сервер, например:

   GroupByUI <- function(id,
                      data,
                      label = 'Group By',
                      selected = 'All',
                      None = FALSE,
                      All = TRUE) {
  renderUI({
    xsecs <- data %>%
      select_if (is.character) %>%
      names()

    if (None) {
      xsecs <- c('None', xsecs)
    }

    if (All == FALSE) {
      xsecs <- xsecs[-which(xsecs == 'All')]

      selectInput(inputId = paste0('groupby_', id),
                  label = label,
                  choices = as.list(xsecs),
                  selected = as.list(xsecs)[1])

    } else {

      selectInput(inputId = paste0('groupby_', id),
                  label = label,
                  choices = as.list(xsecs),
                  selected = selected)
    }
  })

} 

и на сервере у меня есть:

output$GroupBy_Area <- GroupByUI(
  id = "area",
  data = dataFlt()
)

и это работает, когда я загружаю первый CSV (например, Group By selectInput обновляется с правильными именами переменных).

Для контекста dataFlt() — это reactive, для него установлено значение NULL и оно обновляется каждый раз, когда я загружаю новый/другой CSV.

dataFlt <- reactive({
  req(uploadedData$data)
  datafl <- uploadedData$data
})

У меня проблема в том, что если я загрузил CSV, нарисовал данные, а затем изменил загружаемый CSV (например, я изменил данные, которые хочу построить, имена переменных меняются), параметры в группе «Группировать по» selectInput не обновляйте соответствующим образом, ЕСЛИ я не перенесу renderUI непосредственно на сервер:

output$GroupBy_Area <- renderUI({
  req(dataFlt())
  GroupByUI(id = "area", 
            data = dataFlt())
})

и я удаляю renderUI из функции:

GroupByUI <- function(id, data, label = 'Group By', selected = 'All', None = FALSE, All = TRUE) {
  xsecs <- data %>%
    select_if (is.character) %>%
    names()

  if (None) {
    xsecs <- c('None', xsecs)
  }

  if (All == FALSE) {
    xsecs <- xsecs[-which(xsecs == 'All')]

    selectInput(inputId = paste0('groupby_', id),
                label = label,
                choices = as.list(xsecs),
                selected = as.list(xsecs)[1])

  } else {
    selectInput(inputId = paste0('groupby_', id),
                label = label,
                choices = as.list(xsecs),
                selected = selected)
  }
}

Благодаря этому последнему коду (переносу renderUI непосредственно на сервер) он работает каждый раз, когда я меняю загружаемые данные.

Чего я не понимаю, так это почему. Какая разница? И почему оригинальный код работает с первого раза?

Попробуйте вызвать свой первый GroupByUI() в observe()r, чтобы обеспечить ему реактивный контекст.

YBS 12.06.2024 16:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Очень простое решение — передать в функцию GroubByUI сам реактивный объект, а не его значение:

output$GroupBy_Area <- GroupByUI(
  id = "area",
  data = dataFlt
)

И измените функцию, чтобы «вызвать» реактив и получить ее значение:

   GroupByUI <- function(id,
                      data,
                      label = 'Group By',
                      selected = 'All',
                      None = FALSE,
                      All = TRUE) {
  renderUI({
    xsecs <- data() %>%
      select_if (is.character) %>%
      names()

    if (None) {
      xsecs <- c('None', xsecs)
    }

    if (All == FALSE) {
      xsecs <- xsecs[-which(xsecs == 'All')]

      selectInput(inputId = paste0('groupby_', id),
                  label = label,
                  choices = as.list(xsecs),
                  selected = as.list(xsecs)[1])

    } else {

      selectInput(inputId = paste0('groupby_', id),
                  label = label,
                  choices = as.list(xsecs),
                  selected = selected)
    }
  })

} 

Таким образом, функция renderUI зависит от реактива и должна соответствующим образом обновляться.

Объяснение

Нереактивный код в функции сервера на самом деле запускается только один раз, в начале сеанса. Чтобы такие объекты, как renderUI, наблюдатели и т. д., обновлялись правильно, они должны иметь прямую зависимость от самих реактивов. Они получают эту зависимость, если реактивный объект вызывается непосредственно внутри них, но в вашем коде dataFlt() вызывается вашей функцией, которая не работает в реактивном контексте, а не функцией renderUI, которая тогда не принимает зависимости на нем и не обновляется, когда вы считаете, что это необходимо.

Надеюсь, это поможет :)

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