英文:
Recreate reactiveValues in shiny without reactiveness (but same scoping behaviour)
问题
以下是您要翻译的内容:
我有一个拆分成多个模块的Shiny应用程序。为了在这些模块之间传递数据,我使用reactiveVal(ues)
。可以将这些传递给函数,并从其他函数中进行修改。
我想创建一个类似的对象,但不带内置的reactiveValues()
的所有反应功能。我想传递相当大的数据框/表格,并希望手动控制何时以及何时进行数据表/计算。
在此示例中,我想创建一个对象,而不是list()
,它可以像reactiveValues()
一样在不同范围之间传递数据:
library(shiny)
## 模块
module_ui <- function(id) {
ns <- NS(id)
actionButton(inputId = ns("increase"), label = "Increase")
}
module_server <- function(id, vals, not_working) {
moduleServer(id, function(input, output, session) {
observeEvent(input$increase, {
vals$a <- vals$a + 1
not_working$a <- not_working$a + 1
})
})
}
## 主要
ui <- fluidPage(
h1("ReactiveValues Test"),
textOutput(outputId = "out1"),
textOutput(outputId = "out2"),
actionButton(inputId = "update", label = "Update Shown Values"),
module_ui("modid")
)
server <- function(input, output, session) {
vals <- reactiveValues(a = 10)
not_working <- list(a = 10)
observeEvent(input$update, {
output$out1 <- renderText(paste("The value of vals$a is:", vals$a))
output$out2 <- renderText(paste("The value of not_working$a is:", not_working$a))
})
module_server("modid", vals, not_working)
}
shinyApp(ui, server)
在上面的示例中:
- 使用
reactiveValues
的问题在于,按下初始的“Update Shown Values”按钮后,当按下“increase”按钮时,vals$a的值会立即更新,而无需手动“Update Shown Values”。由于我有大量数据并进行一些计算,我不希望它自动触发,也不希望被迫仅在反应环境内读取和修改值。 - 使用
list
的问题在于,该对象被复制到module_server
调用中,因此在主函数范围内不会更新值。
我尝试阅读了reactives.R的源代码,但对我来说R语言的高级内容难以理解正在发生的事情。
我尝试寻找通过引用传递的方法,但尚未找到。但我认为通过引用传递list()
可能会起作用。在Python中,传递字典会以我想要的方式进行操作,其中可以在函数范围内修改键/值对。
我该如何创建一个对象,其作用范围与reactiveValues
相同,但没有所有的反应功能?
是否有任何描述reactives.R源代码中发生的事情的良好资源?(或者不是特定的代码,而是使用的语言功能)
英文:
I have a shiny app split into multiple modules. To pass data between the modules I use reactiveVal(ues)
. These can be passed to functions and modified from within the other functions.
I would like to create a similar object, but without all the reactive capabilities of the built-in reactiveValues()
. I want to pass quite large dataframes/tibbles around and want to manually control if and when datatables/calculations are done.
In this example, I would like to create a object instead of list()
, that can pass data around the scopes in the same way as reactiveValues()
:
library(shiny)
## Module
module_ui <- function(id) {
ns <- NS(id)
actionButton(inputId = ns("increase"), label = "Increase")
}
module_server <- function(id, vals, not_working) {
moduleServer(id, function(input, output, session) {
observeEvent(input$increase, {
vals$a <- vals$a + 1
not_working$a <- not_working$a + 1
})
})
}
## Main
ui <- fluidPage(
h1("ReactiveValues Test"),
textOutput(outputId = "out1"),
textOutput(outputId = "out2"),
actionButton(inputId = "update", label = "Update Shown Values"),
module_ui("modid")
)
server <- function(input, output, session) {
vals <- reactiveValues(a = 10)
not_working <- list(a = 10)
observeEvent(input$update, {
output$out1 <- renderText(paste("The value of vals$a is:", vals$a))
output$out2 <- renderText(paste("The value of not_working$a is:", not_working$a))
})
module_server("modid", vals, not_working)
}
shinyApp(ui, server)
In the example above
- the problem with
reactiveValues
is that after the inital "Update Shown Values" is pressed, the vals$a value is updated instantly when "increase"-button is pressed and does not require the manual "Update Shown Values". Because I have large amounts of data and do some calculation I don't want it to trigger automatically, and I don't want to be forced to only read and modify the value inside reactive environments. - the problem with
list
is that the object gets copied into themodule_server
-call and this the value is not updated in the main function scope.
I have tried to read the source code of reactives.R but it is too advanced R for me to understand what is happening.
I have tried to find a way to pass by reference, but not found one yet. But I think passing a list()
by reference might work. In python passing a dictionary would behave in the way I want. Where key/value-pairs can be modified inside function scopes.
How could I go about created an object which scoping behaves as reactiveValues
, but without all of the reactiveness-functionality?
Are there any good resources describing what is happening in the reactives.R source code? (or not that code specifically but the language-feature used)
答案1
得分: 0
以下是翻译好的部分:
"After some further digging and experimentation, I found that the underlying object that was used was a fastmap. If we wrap it in some S3 object notation we can use it as a replacement for reactiveValues()
without the reactiveness.
This code is mostly copied from reactives.R. All credit goes there.
nolint start: object_name_linter.
dataHolder <- function(...) {
args <- rlang::list2(...)
if ((length(args) > 0) && (is.null(names(args)) || any(names(args) == ""))
rlang::abort("All arguments passed to dataHolder() must be named.")
values <- structure(
list(
impl = fastmap::fastmap()
),
class = "dataholder"
)
lapply(names(args),
(name) {
.subset2(values, "impl")$set(name, args[[name]])
})
values
}
checkName <- function(x) {
if (!is.character(x) || length(x) != 1) {
rlang::abort("Must use single string to index into dataholder.")
}
}
print.dataholder <- function(x, ...) {
cat("
cat(" Values: ", paste0(.subset2(x, "impl")$keys(sort = TRUE), collapse = ", "), "\n")
}
is.dataholder <- function(x) inherits(x, "dataholder")
$.dataholder
<- function(x, name) {
checkName(name)
.subset2(x, "impl")$get(name)
}
[[.dataholder
<- $.dataholder
$<-.dataholder
<- function(x, name, value) {
checkName(name)
.subset2(x, "impl")$set(name, value)
x
}
[[<-.dataholder
<- $<-.dataholder
[.dataholder
<- function(values, name) {
rlang::abort("Can't index dataholder with [
.")
}
[<-.dataholder
<- function(values, name, value) {
rlang::abort("Can't index dataholder with [
.")
}
nolint end"
英文:
After some further digging and experimentation, I found that the underlying object that was used was a fastmap. If we wrap it in some S3 object notation we can use it as a replacement for reactiveValues()
without the reactiveness.
This code is mostly copied from reactives.R. All credit goes there.
# nolint start: object_name_linter.
dataHolder <- function(...) {
args <- rlang::list2(...)
if ((length(args) > 0) && (is.null(names(args)) || any(names(args) == "")))
rlang::abort("All arguments passed to dataHolder() must be named.")
values <- structure(
list(
impl = fastmap::fastmap()
),
class = "dataholder"
)
lapply(names(args),
\(name) {
.subset2(values, "impl")$set(name, args[[name]])
})
values
}
checkName <- function(x) {
if (!is.character(x) || length(x) != 1) {
rlang::abort("Must use single string to index into dataholder.")
}
}
print.dataholder <- function(x, ...) {
cat("<DataHolder>", "\n")
cat(" Values: ", paste0(.subset2(x, "impl")$keys(sort = TRUE), collapse = ", "), "\n")
}
is.dataholder <- function(x) inherits(x, "dataholder")
`$.dataholder` <- function(x, name) {
checkName(name)
.subset2(x, "impl")$get(name)
}
`[[.dataholder` <- `$.dataholder`
`$<-.dataholder` <- function(x, name, value) {
checkName(name)
.subset2(x, "impl")$set(name, value)
x
}
`[[<-.dataholder` <- `$<-.dataholder`
`[.dataholder` <- function(values, name) {
rlang::abort("Can't index dataholder with `[`.")
}
`[<-.dataholder` <- function(values, name, value) {
rlang::abort("Can't index dataholder with `[`.")
}
# nolint end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论