تعمّق في البرمجة التفاعلية
افهم القيم التفاعلية وreactives والمراقبين والرسم البياني التفاعلي
تعمّق في البرمجة التفاعلية درس مجاني في R Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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()) })
}الأسئلة الشائعة
هل درس «تعمّق في البرمجة التفاعلية» مجاني؟
نعم — نص درس «تعمّق في البرمجة التفاعلية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة R Academy، انتقل إلى CoddyKit PRO. تتضمن دورة R Academy 4 دروس في المجموع.
ماذا ستتعلم في «تعمّق في البرمجة التفاعلية»؟
افهم القيم التفاعلية وreactives والمراقبين والرسم البياني التفاعلي تتمرن على R Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ R Academy؟
لا تُشترط خبرة سابقة. R Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «تعمّق في البرمجة التفاعلية»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس R Academy هذا؟
نعم. كل درس في R Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تعمّق في البرمجة التفاعلية
- وحدات Shiny لإعادة استخدام التعليمات البرمجية
- واجهة مستخدم ديناميكية باستخدام renderUI وinsertUI
- نشر تطبيقات Shiny