Zaawansowane programowanie reaktywne
Poznaj wartości reaktywne, obiekty reactive, obserwatory i graf reaktywny.
Zaawansowane programowanie reaktywne to bezpłatna lekcja R Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej R Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs R Academy zawiera 4 lekcji w sumie.
Reaktywność: podstawowa idea
Model programowania reaktywnego w Shiny automatycznie śledzi zależności między obliczeniami. Gdy zmienia się dane wejściowe, ponownie wykonywane są tylko dane wyjściowe, które od nich zależą — nic więcej. Zrozumienie tego grafu zależności jest kluczem do pisania wydajnych i wolnych od błędów aplikacji 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({}) — współdzielone obliczenia
reactive({...}) tworzy wyrażenie reaktywne, którego wynik jest buforowany i współdzielony. Jeśli wiele danych wyjściowych korzysta z tego samego kosztownego obliczenia, należy umieścić je w reactive(), aby wykonywało się raz na zmianę, a nie raz dla każdego odbiorcy. Wywołuje się je jak funkcję: 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() — pojedyncza wartość reaktywna
reactiveVal(initial_value) tworzy zmienną reaktywną, którą można programowo odczytywać i zapisywać. Odczyt następuje przez wywołanie bez argumentów, a zapis — przez wywołanie z nową wartością. Jest to przydatne do przechowywania zmiennego stanu, od którego zależy wielu obserwatorów lub wiele danych wyjściowych.
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() — wiele zmiennych stanu
reactiveValues(key = value, ...) działa jak nazwana lista, której każdy element jest reaktywny. Gdy zmieni się dowolny element, unieważniane są tylko te dane wyjściowe, które odczytują ten element. Używaj tej funkcji do grupowania powiązanych zmiennych stanu aktualizowanych razem.
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() — reagowanie na zdarzenia
observeEvent(eventExpr, handlerExpr) uruchamia handlerExpr za każdym razem, gdy zmieni się eventExpr. Służy do efektów ubocznych, takich jak aktualizowanie stanu, zapisywanie danych w bazie, pobieranie pliku czy przechodzenie do karty. Procedura obsługi nie zwraca wartości.
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() — wartość na żądanie
eventReactive(eventExpr, valueExpr) działa podobnie jak reactive(), ale ponownie oblicza wartość tylko wtedy, gdy wystąpi określone zdarzenie, na przykład kliknięcie przycisku, a nie za każdym razem, gdy zmienią się jej zależności. Należy jej używać do uruchamiania kosztownych obliczeń wyłącznie na wyraźne żądanie użytkownika.
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() — odczyt bez zależności
isolate({expr}) odczytuje wartość reaktywną lub wyrażenie bez ustanawiania zależności reaktywnej. Otaczające obliczenie NIE zostanie ponownie uruchomione, gdy odizolowana wartość się zmieni. Należy używać tej funkcji wewnątrz observe() lub observeEvent(), aby uzyskać dostęp do bieżącego stanu bez subskrybowania zmian.
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() — automatyczne odświeżanie
Umieszczenie invalidateLater(ms) w kontekście reaktywnym powoduje zaplanowanie jego ponownego wykonania po ms milisekundach. Służy to do odpytywania: odświeżania aktualizowanego na żywo źródła danych, aktualizowania zegara lub regularnego sprawdzania, czy w bazie pojawiły się nowe wiersze.
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 — debugowanie grafu reaktywnego
Pakiet reactlog wizualizuje graf zależności reaktywnych. Należy włączyć go przed uruchomieniem aplikacji, a po wykonaniu interakcji wywołać reactlog_show(), aby sprawdzić, które węzły reaktywne zostały unieważnione i w jakiej kolejności. Jest niezbędny podczas debugowania złożonych aplikacji.
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 chainsAntywzorzec izolacji reaktywnej
Częsty błąd polega na przypadkowym odczytaniu wartości reaktywnej w kontekście niereaktywnym lub utworzeniu niepożądanych zależności przez odczyt wartości reaktywnej w obliczeniu, które powinno być odizolowane. Należy zawsze świadomie decydować, gdzie tworzyć zależności, a gdzie odczytywać stan jako jednorazową migawkę.
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() a observeEvent()
observe({...}) tworzy obserwator reaktywny, który jest automatycznie uruchamiany ponownie za każdym razem, gdy zmieni się dowolna odczytywana przez niego wartość reaktywna. observeEvent(event, {...}) zapewnia większą kontrolę: uruchamia się tylko w reakcji na określone zdarzenie. W przypadku efektów ubocznych wywoływanych przyciskiem należy preferować observeEvent(), aby uniknąć niezamierzonego ponownego wykonania.
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)
}Szybkie sprawdzenie
Jaka jest najważniejsza różnica między reactive({}) a eventReactive(event, {})?
Podsumowanie programowania reaktywnego
Najważniejsze informacje z lekcji „Programowanie reaktywne — omówienie zaawansowane”:
reactive({}): buforowane obliczenie, uruchamiane ponownie po zmianie zależności.reactiveVal(init): pojedyncza zmienna reaktywna, której wartość można zmieniać; odczyt za pomocąval(), zapis za pomocąval(new).reactiveValues(...): nazwana lista zmiennych stanu reaktywnego.observeEvent(event, {}): efekty uboczne wywoływane przez określone zdarzenie.eventReactive(event, {}): wartość obliczana tylko po wystąpieniu określonego zdarzenia.isolate({}): odczyt wartości reaktywnej bez tworzenia zależności.invalidateLater(ms): planowanie okresowego ponownego wykonania na potrzeby aktualizacji na żywo.- Używaj
reactlogdo wizualizacji i debugowania grafu zależności reaktywnych.
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()) })
}Często zadawane pytania
Czy lekcja „Zaawansowane programowanie reaktywne” jest bezpłatna?
Tak — pełny tekst „Zaawansowane programowanie reaktywne” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu R Academy, przejdź na CoddyKit PRO. Kurs R Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Zaawansowane programowanie reaktywne”?
Poznaj wartości reaktywne, obiekty reactive, obserwatory i graf reaktywny. Ćwiczysz R Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć R Academy?
Nie wymagamy żadnego doświadczenia. R Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Zaawansowane programowanie reaktywne”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji R Academy?
Tak. Każda lekcja R Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Zaawansowane programowanie reaktywne
- Moduły Shiny wielokrotnego użytku
- Dynamiczny interfejs za pomocą renderUI i insertUI
- Wdrażanie aplikacji Shiny