如何等待输入小部件更新直到在 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 包中看到了这种行为。)但是,我不知道如何解决这个问题。我尝试了一堆功能,例如 isolatereq,但没有成功。

为避免不必要地触发反应或输出,在 :

中使用 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