深入理解响应式编程
理解响应式值、响应式表达式、观察者和响应式图
深入理解响应式编程 是 CoddyKit 上的免费 R Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。
响应式:核心思想
Shiny 的响应式编程模型会自动跟踪各项计算之间的依赖关系。当输入发生变化时,只有依赖于该输入的输出会重新执行,其他内容不会执行。理解这一依赖关系图,是编写高效且无错误 Shiny 应用的关键。
library(shiny)
# Simplest reactive relationship:
# output depends on input — Shiny tracks this automatically
server <- function(input, output, session) {
output$result <- renderText({
paste('You typed:', input$text_in) # depends on text_in
})
}reactive({})——共享计算
reactive({...}) 会创建一个响应式表达式,其结果会被缓存并共享。如果多个输出使用同一个开销较大的计算,请将其封装在 reactive() 中,这样每次变化只需计算一次,而不是为每个使用者分别计算。调用方式与函数相同:data_subset()。
server <- function(input, output, session) {
# Computed once, shared by multiple outputs
filtered_data <- reactive({
subset(mtcars, cyl == input$cyl_select)
})
output$plot <- renderPlot({ plot(filtered_data()$wt,
filtered_data()$mpg) })
output$table <- renderTable({ filtered_data() })
# filtered_data() is only recomputed once when cyl_select changes
}reactiveVal()——单个响应式值
reactiveVal(initial_value) 会创建一个可以通过程序读取和写入的响应式变量。不带参数调用它即可读取;使用新值调用它即可写入。当多个观察者或输出依赖于某个可变状态时,这种方式非常有用。
server <- function(input, output, session) {
counter <- reactiveVal(0) # initial value = 0
observeEvent(input$increment, {
counter(counter() + 1) # read current, write new
})
observeEvent(input$reset, {
counter(0) # reset to 0
})
output$count <- renderText(paste('Count:', counter()))
}reactiveValues()——多个状态变量
reactiveValues(key = value, ...) 类似于一个命名列表,其中每个元素都是响应式的。当某个元素发生变化时,只有读取该元素的输出会失效。请使用它将需要一起更新的相关状态变量组合起来。
server <- function(input, output, session) {
state <- reactiveValues(
data = NULL,
current_page = 1,
total_rows = 0
)
observeEvent(input$load_btn, {
state$data <- read.csv(input$file$datapath)
state$total_rows <- nrow(state$data)
state$current_page <- 1
})
output$info <- renderText({
paste('Page', state$current_page, '| Rows:', state$total_rows)
})
}observeEvent()——响应事件
observeEvent(eventExpr, handlerExpr) 会在 eventExpr 发生变化时运行 handlerExpr。它用于执行副作用,例如更新状态、写入数据库、下载文件或切换选项卡。处理程序不会返回值。
server <- function(input, output, session) {
observeEvent(input$save_btn, {
# Runs once each time save_btn is clicked
write.csv(current_data(), '/tmp/export.csv', row.names = FALSE)
showNotification('Saved!', type = 'message')
})
observeEvent(input$reset_btn, {
updateTextInput(session, 'search', value = '')
updateSliderInput(session, 'range', value = c(0, 100))
})
}eventReactive() — 按需计算值
eventReactive(eventExpr, valueExpr) 类似于 reactive(),但只有在特定事件触发时(例如按钮点击)才会重新计算,而不是每当依赖项发生变化时都重新计算。对于开销较大的计算,请使用它,以便仅在用户明确请求时运行。
server <- function(input, output, session) {
# Only re-run the model when 'Run Model' is clicked
model_result <- eventReactive(input$run_btn, {
# Expensive computation — only on button click
lm(as.formula(input$formula), data = get(input$dataset))
})
output$summary <- renderPrint({
summary(model_result())
})
}isolate() — 无依赖读取
isolate({expr}) 读取响应式值或表达式,但不会建立响应式依赖。被包围的计算不会因为隔离值发生变化而重新运行。请在 observe() 或 observeEvent() 中使用它,以便访问当前状态而不订阅变化。
server <- function(input, output, session) {
observeEvent(input$add_row_btn, {
# Read current_data without depending on it
current <- isolate(data_rv())
new_row <- data.frame(
id = nrow(current) + 1,
value = input$new_value
)
data_rv(rbind(current, new_row))
})
}invalidateLater() — 自动刷新
在响应式上下文中使用 invalidateLater(ms),会安排该上下文在 ms 毫秒后重新执行。这可用于轮询:刷新实时数据流、更新时间,或定期检查数据库中是否有新行。
server <- function(input, output, session) {
live_data <- reactive({
invalidateLater(5000) # re-run every 5 seconds
# Fetch fresh data from external source
httr2::request('https://api.example.com/stats') |>
httr2::req_perform() |>
httr2::resp_body_json()
})
output$live_plot <- renderPlot({
plot(live_data()$time, live_data()$value, type = 'l')
})
}reactlog — 调试响应式图
reactlog 软件包可将响应式依赖图可视化。请在运行应用前启用它,并在交互后调用 reactlog_show(),以查看哪些响应式节点失效以及失效的顺序。对于调试复杂应用来说,这一功能必不可少。
library(reactlog)
# Enable reactlog BEFORE launching the app
reactlog_enable()
# Launch your app
shinyApp(ui, server)
# After interacting in the browser, in the R console:
shiny::reactlogShow()
# A viewer opens showing the reactive graph
# with timestamps and invalidation chains响应式隔离反模式
一种常见错误是:在非响应式上下文中意外读取响应式值,或者在本应隔离的计算中读取响应式值,从而创建不需要的依赖。请始终明确区分:哪些位置需要创建依赖,哪些位置只需将状态读取为一次性快照。
server <- function(input, output, session) {
# WRONG: output depends on input$name, but also re-runs
# every time input$slider changes (unintended dependency)
output$msg <- renderText({
paste(input$name, 'total:', input$slider * 2)
})
# CORRECT: output only responds to input$name changes;
# slider is read as a snapshot
output$msg_correct <- renderText({
slider_val <- isolate(input$slider)
paste(input$name, 'snapshot total:', slider_val * 2)
})
}observe() 与 observeEvent()
observe({...}) 会创建一个响应式观察者,只要它读取的任何响应式值发生变化,就会自动重新运行。observeEvent(event, {...}) 的控制更精确:它只会在特定事件发生时触发。对于由按钮驱动的副作用,优先使用 observeEvent(),以避免意外的重复执行。
server <- function(input, output, session) {
# observe: re-runs on ANY change in input$x or input$y
observe({
cat('x or y changed:', input$x, input$y, '\n')
})
# observeEvent: only runs when button is clicked
observeEvent(input$submit_btn, {
cat('Form submitted with x =', isolate(input$x), '\n')
}, ignoreNULL = TRUE, ignoreInit = TRUE)
}快速检查
reactive({}) 与 eventReactive(event, {}) 的关键区别是什么?
响应式编程回顾
响应式编程深入学习的要点:
reactive({}):缓存计算结果,在依赖项发生变化时重新运行。reactiveVal(init):单个可变的响应式变量;使用val()读取,使用val(new)写入。reactiveValues(...):由名称标识的响应式状态变量列表。observeEvent(event, {}):由特定事件触发的副作用。eventReactive(event, {}):仅在特定事件触发时计算值。isolate({}):读取响应式值而不创建依赖。invalidateLater(ms):安排定期重新执行,以实现实时更新。- 使用
reactlog将响应式依赖图可视化并进行调试。
server <- function(input, output, session) {
# State
rv <- reactiveValues(data = NULL, n = 0)
# Load data on button click
observeEvent(input$load, {
rv$data <- read.csv(input$file$datapath)
rv$n <- nrow(rv$data)
})
# Expensive model only on 'Run' click
model <- eventReactive(input$run, {
lm(y ~ ., data = isolate(rv$data))
})
output$summary <- renderPrint({ summary(model()) })
}常见问题解答
「深入理解响应式编程」课时是免费的吗?
是的 — 「深入理解响应式编程」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。
「深入理解响应式编程」这节课中我会学到什么?
理解响应式值、响应式表达式、观察者和响应式图 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 R Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「深入理解响应式编程」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 R Academy 课中编写并运行代码吗?
能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。