首页 > 解决方案 > 防止使用两个依赖路径双重执行闪亮的反应

问题描述

考虑以下闪亮的应用程序。用户可以更改三个基本输入:AMS。“内容” C(在verbatimTextOuput右侧)直接取决于AS

在此处输入图像描述

S可以通过两种方式进行更改:由用户或通过更改M/ A。如果用户改变了S,那么依赖就M无关紧要了。M如果为空,也不会使用。

情况如下图所示。

依赖关系图

问题是 whenM不是空白并且A被更改:

  1. C根据新旧 S更新A
  2. S更新基于M和新的A
  3. C根据 newS和 new得到更新A

因此,C更新两次,第一次使用无效值。

想要发生的是S基于 new进行更新A,然后C基于 newS和 new进行更新A

要查看问题,请运行应用程序,然后:

  1. 把东西放进M盒子里
  2. 改变A
  3. 观察到C改变了两次。

如何阻止第一次更新?

谢谢!


闪亮的应用程序代码:

library(shiny)
library(digest)

ui <- fluidPage(

    sidebarLayout(
        sidebarPanel(
           textInput("M", "M:", ""), 
           textInput("S", "S:", "testS"),
           selectInput("A", "A:", c("A1","A2"))
        ),
        
        mainPanel(
           verbatimTextOutput("C")
        )
    )
)

server <- function(input, output, session) {
    
    # Count calculations of C
    count = 0
    
    # Make reactive so we can modify the value
    # in the input box (inpt$S is an input and output,
    # essentially)
    S <- reactive({
        input$S
    })
    
    # Create "content" from A and S
    C <- reactive({
            count <<- count + 1
            Sys.sleep(0.5)
            message("Count ", count)
            paste(
                "Count: ", count, "\n", digest::sha1( c(input$A, S()) )
                )
    })

    # When M changes, we need to change S based on A and M
    # OR set S to a default value
    observeEvent(input$M, {
        
        # If user gets rid of M, reset S to default
        if(input$M == ""){
            S = "testS"
        }else{
            S = digest::sha1(c(input$M,input$A))
        }
        
        # Update the input to new S
        updateTextInput(
            session, 
            "S",
            value = S
        )
    })

    # When A changes, we need to change S based on A and M
    # OR if M is blank, do nothing (S doesn't depend on M if M is blank)
    observeEvent(input$A, {
    
        # If there's no M, don't use it
        if(input$M == "") return()
        
        # Update the input to new S
        updateTextInput(
            session, 
            "S",
            value = digest::sha1(c(input$M,input$A))
        )
    })
    
    # "Content"
    output$C <- renderText({
        C()
    })
    
}

shinyApp(ui = ui, server = server)

标签: rshinyreactiveshiny-reactivity

解决方案


我认为这里的问题是当你应该使用反应器时你正在使用观察者。通常,您只想使用观察者来获得副作用(保存文件,按下按钮),而不是在您想要应用程序中的值时。在这里,我认为最好使用renderUI响应式生成 UI 元素,而不是使用观察者更新它。

library(shiny)
library(digest)

ui <- fluidPage(
  
  sidebarLayout(
    sidebarPanel(
      textInput("M", "M:", ""), 
      uiOutput("S_UI"),
      selectInput("A", "A:", c("A1","A2"))
    ),
    
    mainPanel(
      verbatimTextOutput("C")
    )
  )
)

server <- function(input, output, session) {
  
  # Count calculations of C
  count = 0

  # Create "content" from A and S
  C <- reactive({
    count <<- count + 1
    Sys.sleep(0.5)
    message("Count ", count)
    paste(
      "Count: ", count, "\n", digest::sha1( c(input$A, input$S ))
    )
  })
  
  output$S_UI <- renderUI({
    if (input$M == "") {
      val <- "testS"
    } else {
      val <- "S"
    }
    return(textInput("S", "S:", val))
  })
  
  # "Content"
  output$C <- renderText({
    C()
  })
}

shinyApp(ui = ui, server = server)

推荐阅读