防止使用两个依赖路径双重执行 shiny reactive
prevent double execution of shiny reactive with two dependency pathways
考虑以下闪亮的应用程序。用户可以更改三个基本输入:A
、M
和 S
。 “内容”C(右边verbatimTextOuput
中)直接依赖于A
和S
.
S
可以通过两种方式更改:由用户更改,或通过更改 M
/A
。如果用户更改 S
,则对 M
的依赖性无关紧要。 M
如果为空也不使用。
情况如下图所示。
问题是当 M
不为空并且 A
被更改时:
C
根据 old S
和新的 A
进行更新
S
基于 M
和新的 A
进行更新
C
根据新的 S
和新的 A
. 进行更新
因此,C
被更新了两次,第一次是无效值。
我希望发生的是 S
更新,基于新的 A
,然后 C
更新基于新 S
和新 A
.
要查看问题,运行 应用程序,然后:
- 在
M
盒子里放东西
- 改变
A
- 观察
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)
我认为这里的问题是你在应该使用反应式的时候使用了观察者。通常,您只想使用观察器来产生副作用(保存文件、按下按钮),而不是在您需要应用程序中的值时使用。在这里我认为最好使用 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)
这应该可行(如果我理解您的函数的逻辑)。它有一堆额外的消息,因此您可以查看更新的时间和顺序。
(我还更改了 count
的超赋值,因为那些只会让我烦恼,但我知道它对 isolate 有点尴尬,所以请随意放回去;)
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 = reactiveVal(0)
S = reactiveVal("testS")
observeEvent(input$S, { message("S updated externally")
S(input$S)
})
# When M changes, we need to change S based on A and M
# OR set S to a default value
observeEvent(input$M, { message("M updated")
# 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(inputId = "S", value = S())
message("S updated by M")
})
# 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, { message("A updated")
# If there's no M, don't use it
req(input$M)
# Update the input to new S
S(digest::sha1(c(input$M, input$A)))
updateTextInput(inputId = "S", value = S())
message("S updated by A")
})
# "Content"
output$C <- renderText({
n = isolate(count()) + 1
count(n)
#Sys.sleep(0.5)
message("Count ", n)
paste("Count: ", n, "\n", digest::sha1( c(input$A, S())))
})
}
shinyApp(ui = ui, server = server)
当用户通过 input$M
或 input$A
从外部或内部更改 input$S
时,S()
reactiveVal 会更新。注释显示了哪些更改了值,output$C
仅在 input$A
或 S()
更改时更改。
考虑以下闪亮的应用程序。用户可以更改三个基本输入:A
、M
和 S
。 “内容”C(右边verbatimTextOuput
中)直接依赖于A
和S
.
S
可以通过两种方式更改:由用户更改,或通过更改 M
/A
。如果用户更改 S
,则对 M
的依赖性无关紧要。 M
如果为空也不使用。
情况如下图所示。
问题是当 M
不为空并且 A
被更改时:
C
根据 oldS
和新的A
进行更新
S
基于M
和新的A
进行更新
C
根据新的S
和新的A
. 进行更新
因此,C
被更新了两次,第一次是无效值。
我希望发生的是 S
更新,基于新的 A
,然后 C
更新基于新 S
和新 A
.
要查看问题,运行 应用程序,然后:
- 在
M
盒子里放东西 - 改变
A
- 观察
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)
我认为这里的问题是你在应该使用反应式的时候使用了观察者。通常,您只想使用观察器来产生副作用(保存文件、按下按钮),而不是在您需要应用程序中的值时使用。在这里我认为最好使用 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)
这应该可行(如果我理解您的函数的逻辑)。它有一堆额外的消息,因此您可以查看更新的时间和顺序。
(我还更改了 count
的超赋值,因为那些只会让我烦恼,但我知道它对 isolate 有点尴尬,所以请随意放回去;)
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 = reactiveVal(0)
S = reactiveVal("testS")
observeEvent(input$S, { message("S updated externally")
S(input$S)
})
# When M changes, we need to change S based on A and M
# OR set S to a default value
observeEvent(input$M, { message("M updated")
# 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(inputId = "S", value = S())
message("S updated by M")
})
# 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, { message("A updated")
# If there's no M, don't use it
req(input$M)
# Update the input to new S
S(digest::sha1(c(input$M, input$A)))
updateTextInput(inputId = "S", value = S())
message("S updated by A")
})
# "Content"
output$C <- renderText({
n = isolate(count()) + 1
count(n)
#Sys.sleep(0.5)
message("Count ", n)
paste("Count: ", n, "\n", digest::sha1( c(input$A, S())))
})
}
shinyApp(ui = ui, server = server)
当用户通过 input$M
或 input$A
从外部或内部更改 input$S
时,S()
reactiveVal 会更新。注释显示了哪些更改了值,output$C
仅在 input$A
或 S()
更改时更改。