如何等待输入小部件更新直到在 Shiny 中呈现输出?
How to wait for input widget updates until rendering output in Shiny?
请参阅以下类似于问题的废话最小工作示例:
library(shiny)
ui = shiny::fluidPage(
shiny::sidebarLayout(
shiny::sidebarPanel(
shiny::radioButtons("type", "Type", choices = c("5", "15", "21")),
shiny::sliderInput("x", "x", min = 1, max = 10, value = 7),
shiny::checkboxInput("auto","Set y to 8 when type is 15, and 9 when type is 21", value = TRUE),
shiny::sliderInput("y", "y", min = 1, max = 10, value = 7)
),
shiny::mainPanel(
shiny::plotOutput("show")
)
)
)
server = function(input, output, session) {
plotData = shiny::reactive({
list(x = input$x, y = input$y, type = input$type)
})
output$show = renderPlot({
shiny::req(plotData())
d = plotData()
print("refresh")
plot(d$x,d$y, pch = as.integer(d$type))
})
observe({
n = input$n
a = input$auto
type = input$type
if (type == "a") return(NULL)
if (a) shiny::updateSliderInput(session, "y", value = switch(type, "15" = 8, "21" = 9))
})
}
shiny::shinyApp(ui = ui, server = server)
问题是,当用户选择类型 15 或 21 时,绘图会渲染两次。这是因为当 plotData
更改时,updateSliderInput
在 [=13] 之前执行=]. (我在 reactlog
包中看到了这种行为。)但是,我不知道如何解决这个问题。我尝试了一堆功能,例如 isolate
、req
,但没有成功。
为避免不必要地触发反应或输出,在 shiny:
中使用 update*
函数时,您应该几乎总是使用 freezeReactiveValue
library(shiny)
ui = shiny::fluidPage(
shiny::sidebarLayout(
shiny::sidebarPanel(
shiny::radioButtons("type", "Type", choices = c("5", "15", "21")),
shiny::sliderInput("x", "x", min = 1, max = 10, value = 7),
shiny::checkboxInput("auto","Set y to 8 when type is 15, and 9 when type is 21", value = TRUE),
shiny::sliderInput("y", "y", min = 1, max = 10, value = 7)
),
shiny::mainPanel(
shiny::plotOutput("show")
)
)
)
server = function(input, output, session) {
plotData = shiny::reactive({
list(x = input$x, y = input$y, type = input$type)
})
observe({
n = input$n
a = input$auto
type = input$type
if (type == "a") return(NULL)
freezeReactiveValue(input, "y")
if (a) shiny::updateSliderInput(session, "y", value = switch(type, "15" = 8, "21" = 9))
}, priority = 1)
output$show = renderPlot({
shiny::req(plotData())
d = plotData()
print("refresh")
plot(d$x,d$y, pch = as.integer(d$type))
})
}
shiny::shinyApp(ui = ui, server = server)
请参阅 related chapter 来自 Mastering Shiny。
请参阅以下类似于问题的废话最小工作示例:
library(shiny)
ui = shiny::fluidPage(
shiny::sidebarLayout(
shiny::sidebarPanel(
shiny::radioButtons("type", "Type", choices = c("5", "15", "21")),
shiny::sliderInput("x", "x", min = 1, max = 10, value = 7),
shiny::checkboxInput("auto","Set y to 8 when type is 15, and 9 when type is 21", value = TRUE),
shiny::sliderInput("y", "y", min = 1, max = 10, value = 7)
),
shiny::mainPanel(
shiny::plotOutput("show")
)
)
)
server = function(input, output, session) {
plotData = shiny::reactive({
list(x = input$x, y = input$y, type = input$type)
})
output$show = renderPlot({
shiny::req(plotData())
d = plotData()
print("refresh")
plot(d$x,d$y, pch = as.integer(d$type))
})
observe({
n = input$n
a = input$auto
type = input$type
if (type == "a") return(NULL)
if (a) shiny::updateSliderInput(session, "y", value = switch(type, "15" = 8, "21" = 9))
})
}
shiny::shinyApp(ui = ui, server = server)
问题是,当用户选择类型 15 或 21 时,绘图会渲染两次。这是因为当 plotData
更改时,updateSliderInput
在 [=13] 之前执行=]. (我在 reactlog
包中看到了这种行为。)但是,我不知道如何解决这个问题。我尝试了一堆功能,例如 isolate
、req
,但没有成功。
为避免不必要地触发反应或输出,在 shiny:
中使用update*
函数时,您应该几乎总是使用 freezeReactiveValue
library(shiny)
ui = shiny::fluidPage(
shiny::sidebarLayout(
shiny::sidebarPanel(
shiny::radioButtons("type", "Type", choices = c("5", "15", "21")),
shiny::sliderInput("x", "x", min = 1, max = 10, value = 7),
shiny::checkboxInput("auto","Set y to 8 when type is 15, and 9 when type is 21", value = TRUE),
shiny::sliderInput("y", "y", min = 1, max = 10, value = 7)
),
shiny::mainPanel(
shiny::plotOutput("show")
)
)
)
server = function(input, output, session) {
plotData = shiny::reactive({
list(x = input$x, y = input$y, type = input$type)
})
observe({
n = input$n
a = input$auto
type = input$type
if (type == "a") return(NULL)
freezeReactiveValue(input, "y")
if (a) shiny::updateSliderInput(session, "y", value = switch(type, "15" = 8, "21" = 9))
}, priority = 1)
output$show = renderPlot({
shiny::req(plotData())
d = plotData()
print("refresh")
plot(d$x,d$y, pch = as.integer(d$type))
})
}
shiny::shinyApp(ui = ui, server = server)
请参阅 related chapter 来自 Mastering Shiny。