เจาะลึกการเขียนโปรแกรมแบบรีแอกทีฟ
ทำความเข้าใจค่ารีแอกทีฟ ออบเซิร์ฟเวอร์ และกราฟรีแอกทีฟ
เจาะลึกการเขียนโปรแกรมแบบรีแอกทีฟ เป็นบทเรียน R Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน 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) จะเรียกใช้ handlerExpr ทุกครั้งที่ eventExpr เปลี่ยนแปลง ใช้สำหรับการทำงานข้างเคียง เช่น การอัปเดตสถานะ การเขียนลงฐานข้อมูล การดาวน์โหลดไฟล์ หรือการเปลี่ยนไปยังแท็บอื่น ฟังก์ชันจัดการจะไม่ส่งคืนค่า
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 ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส R Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เจาะลึกการเขียนโปรแกรมแบบรีแอกทีฟ”
ทำความเข้าใจค่ารีแอกทีฟ ออบเซิร์ฟเวอร์ และกราฟรีแอกทีฟ คุณปฏิบัติ R Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน R Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน R Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “เจาะลึกการเขียนโปรแกรมแบบรีแอกทีฟ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน R Academy นี้ได้ไหม
ได้ บทเรียน R Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เจาะลึกการเขียนโปรแกรมแบบรีแอกทีฟ
- โมดูล Shiny เพื่อการนำโค้ดกลับมาใช้ใหม่
- ส่วนติดต่อผู้ใช้แบบไดนามิกด้วย renderUI และ insertUI
- การนำแอป Shiny ไปใช้งาน