0Pricing
R Academy · Lekcja

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 chains

Antywzorzec 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 reactlog do 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

  1. Zaawansowane programowanie reaktywne
  2. Moduły Shiny wielokrotnego użytku
  3. Dynamiczny interfejs za pomocą renderUI i insertUI
  4. Wdrażanie aplikacji Shiny
← Powrót do R Academy