Build Reactivity
Overview
在Shiny中,响应式编程中包含三种对象,这些对象用以下符号表示:
- reactive source & endpoint:最简单的响应式编程结构,只包括一个源点和一个终点。
在Shiny应用程序中,the source通常是通过浏览器界面输入的用户。 例如,当用户选择一个项目,键入输入或单击按钮时,这些操作将设置作为reactive source的值。 reactive endpoint通常是出现在用户浏览器窗口中的东西,例如图形或值表。
在一个简单的Shiny应用程序中,可通过输入对象可访问reactive source,并通过输出对象可访问reactive endpoint。 (实际上,还有其他可能的source和endpoint,我们将在后面讨论,但现在我们仅讨论输入和输出。)
01_hello示例使用具有一个source和一个endpoint的这种简单结构。 该示例的服务器功能代码如下所示:
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs))
})
}
output$distPlot对象是reactive endpoint,它使用reactive source (input$obs)。 每当input$obs更改时,就会通知output$distPlot它需要重新执行。 在具有响应式用户界面的传统程序中,这可能涉及设置事件处理程序以及编写代码以读取值和传输数据。 Shiny在幕后为您完成所有这些操作,因此您只需编写看起来像常规R代码的代码即可。
一个reactive source可以连接到多个endpoint,反之亦然。 这是稍微复杂一些的Shiny应用程序的服务器功能:
server <- function(input, output) {
output$plotOut <- renderPlot({
hist(faithful$eruptions, breaks = as.numeric(input$nBreaks))
if (input$individualObs)
rug(faithful$eruptions)
})
output$tableOut <- renderTable({
if (input$individualObs)
faithful
else
NULL
})
}
在Shiny应用程序中,无需明确描述这些关系中的每个关系,而无需告诉R当每个输入组件发生变化时该怎么做; Shiny会自动为您处理这些详细信息。
在具有上述结构的应用程序中,只要input$nBreaks的值发生更改,生成图的表达式将自动重新执行。 每当input$individualObs的值更改时,绘图和表格函数将自动重新执行。 (在Shiny应用程序中,大多数终结点函数的结果都会自动打包并发送到Web浏览器。)
- reactive conductors
到目前为止,我们已经看到了reactive source和reactive endpoint,大多数简单示例仅使用这两个组件,将source直接连接到endpoint。也可以在source和endpoint之间放置电抗组件。这些组件称为reactive conductors。
reactive conductors既可以是依存关系,也可以有依存关系。换句话说,在响应式结构图中,它既可以是父代,也可以是子代。在响应式图中,source只能是父代(他们可以有依存关系),endpoint只能是子代(他们是依存关系)。
reactive conductors对于慢封装或高开销的计算操作可能很有用。例如,假设您有一个应用程序,该应用程序使用值input$n并在Fibonacci序列中打印_n_th值,并在序列中将_n_th的值反加一(请注意,这些示例中的代码已精简以说明反应性概念,不一定代表编码最佳实践):
# Calculate nth number in Fibonacci sequence
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
server <- function(input, output) {
output$nthValue <- renderText({ fib(as.numeric(input$n)) })
output$nthValueInv <- renderText({ 1 / fib(as.numeric(input$n)) })
}
该app的结构图如下所示:
fib()算法的效率很低,除非必要,我们不希望花费太长的时间来运行它。 但是在这个应用程序中,我们运行了两次! 在相当快的现代计算机上,将input$n设置为30大约需要15秒才能计算出答案,这主要是因为fib()运行了两次。
可以通过在source和endpoint之间添加reactive conductor来减少计算量:
fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))
server <- function(input, output) {
currentFib <- reactive({ fib(as.numeric(input$n)) })
output$nthValue <- renderText({ currentFib() })
output$nthValueInv <- renderText({ 1 / currentFib() })
}
新的结构图如下:
请记住,如果您的应用程序尝试在响应式上下文之外(即,在reactive expression或observer之外)访问响应式值或表达式,则将导致错误。 您可以想象存在一个可以看到和改变非响应式地盘的响应式“地盘”,但是非响应式地盘不能对响应式地盘做同样的事情。 像这样的代码将不起作用,因为对fib()的调用不在响应式地盘中(不在active()或renderXX()调用中),但是它尝试访问的是响应式值input$n:
server <- function(input, output) {
# Will give error
currentFib <- fib(as.numeric(input$n))
output$nthValue <- renderText({ currentFib })
}
举个不恰当的例子,可能就像是C语言中不能int n; scanf("%d", &n); int a[n];?
另一方面,如果currentFib是访问非响应式的函数,并且该函数在非响应式地盘中被调用,则它将起作用:
server <- function(input, output) {
# OK, as long as this is called from the reactive world:
currentFib <- function() {
fib(as.numeric(input$n))
}
output$nthValue <- renderText({ currentFib() })
}
相对的,如果是定义了const int n = 1000;就可以了?
总结:
在本节中,我们了解到:
- reactive source可以向下游示意它们需要重新执行。
- Reactive conductors放置在响应式图上sources和endpoints之间的某个位置。 它们通常用于封装慢操作。
- Reactive endpoints可以被Reactive enviornment重新执行,并且可以请求上游对象执行。
- 无效箭头(Invalidation arrows)表示无效事件的流程。 也可以说子节点是父节点的依赖,或与父节点之间有依赖(child node is a dependent of or takes a dependency on the parent node)。
Implementations of sources, conductors, and endpoints: values, expressions, and observers
我们已经讨论了reactive sources,conductors和endpoints。 这些是在响应式程序中扮演特定角色的零件的通用术语。 当前,Shiny具有一类充当reactive sources的对象,一类充当reactive conductors的对象以及一类充当reactive endpoints的对象,但是原则上可以有其他类别实现这些角色。
- Reactive values are an implementation of Reactive sources; that is, they are an implementation of that role. 响应式值是响应式源的实现;它们是reactive sources的实现。
- Reactive expressions are an implementation of Reactive conductors. They can access reactive values or other reactive expressions, and they return a value. 响应式表达式是Reactive conductors的一种实现,他们可以访问响应式值或其他响应式表达式,并返回一个值。
- Observers are an implementation of Reactive endpoints. They can access reactive sources and reactive expressions, and they don’t return a value; they are used for their side effects. 观察者是Reactive endpoints的实现。 他们可以访问响应式源和响应式表达式,并且不返回值;
所有示例均使用这三种实现,因为目前没有其他sources,conductors和endpoints角色的实现。
Reactive values
响应式值包含值(毫不奇怪),其他响应式对象可以读取该值。 输入对象是一个ReactiveValues对象,它看起来像一个列表,并且包含许多单独的响应式值。 输入中的值是通过Web浏览器的输入设置的。
Reactive expressions
通过上面的斐波那契示例,我们已经看到了响应式表达式在起作用。 他们缓存其返回值,以使应用程序更有效地运行。 请注意,抽象地说,reactive conductors不一定要缓存返回值,但是在此实现中,reactive conductors确实可以。
响应式表达式可用于缓存响应用户输入而发生的任何过程的结果,包括:
- 访问数据库
- 从文件读取数据
- 通过网络下载数据
- 执行高开销的计算
Observers
观察者类似于响应式表达,但有一些重要的区别。 与响应式一样,它们可以访问响应式值和响应式表达式。 但是,它们不返回任何值,因此不缓存其返回值。 它们没有返回值,而是有副作用–通常,这涉及将数据发送到Web浏览器。
输出对象看起来像一个列表,它可以包含许多单独的观察者。
如果您看一下renderText()的代码,朋友,你会发现他们每个都返回一个函数,该函数返回一个值。 通常按以下方式使用它们:
output$number <- renderText({ as.numeric(input$n) + 1 })
Differences between reactive expressions and observers
响应式表达式和观察者的相似之处在于它们存储可以执行的表达式,但是它们之间存在一些根本差异。
- 观察者(通常是endpoint)对响应式刷新事件做出响应,而响应式表达式(通常是conductors)则不响应。 我们将在下一部分中详细了解刷新事件。 如果要执行反应式表达式,则它必须具有观察者作为响应式依赖图上的子代。
- 响应式表达式返回值,但观察者则不。
来源:CSDN
作者:your sugar
链接:https://blog.csdn.net/qq_37430374/article/details/103712772