这解决了您提出的问题,但修改非反应性变量也会导致问题。一般来说,考虑在shiny 环境中修改非反应性变量表明您可能没有考虑如何扩展或存储或正确维护应用程序状态。例如,如果您希望有多个用户,那么要知道此数据不与其他用户共享,它只是当前用户。使用局部变量无法解决这个问题。 (如果您需要在用户之间“共享”某些内容,您确实需要一个数据后端,例如某种形式的 SQL,包括 SQLite。请参阅:https://shiny.rstudio.com/articles/persistent-data-storage.html)
除了所有其他 shiny 教程之外,我建议您阅读有关 variable scope 的闪亮说明,特别是诸如
-
“一个只读数据集,将在 Shiny 启动时加载一次,并且可供每个用户会话使用”,谈论存储在
server 函数定义之外的数据;
-
“[这个变量]的这个本地副本在其他会话中不可见”,谈论存储在
server函数中的变量;和
-
“在
global.R 中定义的对象与在服务器函数定义之外的app.R 中定义的对象类似”。
话虽如此,有两个提供了解决方案。
反应式框架(鼓励!)
library(shiny)
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
sidebarLayout(
sidebarPanel(
textInput(inputId = "sometext", label = "Some Text:",
placeholder = "(nothing meaningful)"),
actionButton(inputId = "addnow", label = "Add!")
),
mainPanel(
tableOutput("myframe")
)
)
)
server <- function(input, output, session) {
rxframe <- reactiveVal(
data.frame(txt = character(0), i = integer(0),
stringsAsFactors = FALSE)
)
observeEvent(input$addnow, {
newdat <- data.frame(txt = isolate(input$sometext),
i = nrow(rxframe()) + 1L,
stringsAsFactors = FALSE)
rxframe( rbind(rxframe(), newdat, stringsAsFactors = FALSE) )
})
output$myframe <- shiny::renderTable( rxframe() )
}
shinyApp(ui, server)
此示例使用shiny::reactiveVal,尽管使用shiny::reactiveValues 也同样容易(如果正在使用多个反应变量)。
非反应性框架(不鼓励)
library(shiny)
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
sidebarLayout(
sidebarPanel(
textInput(inputId = "sometext", label = "Some Text:",
placeholder = "(nothing meaningful)"),
actionButton(inputId = "addnow", label = "Add!")
),
mainPanel(
tableOutput("myframe")
)
)
)
server <- function(input, output, session) {
nonrxframe <- data.frame(txt = character(0), i = integer(0),
stringsAsFactors = FALSE)
output$myframe <- shiny::renderTable({
req(input$addnow)
nonrxframe <<- rbind(nonrxframe,
data.frame(txt = isolate(input$sometext),
i = nrow(nonrxframe) + 1L,
stringsAsFactors = FALSE),
stringsAsFactors = FALSE)
nonrxframe
})
}
shinyApp(ui, server)
两者都允许排序,如下面的屏幕截图所示。许多人会争辩说,第一个(反应式)示例更清洁、更安全。 (只是使用<<- 对我来说就足够了!)