---
title: "Get started with reactives"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Get started with reactives}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

An app often holds a set of reactives that changes while it runs, such as one for each module the user adds. The set needs keys, so that one of its reactives can be read, replaced or removed without disturbing the others. The shiny package offers `reactiveValues()` for keyed stored values, but it can neither remove nor reorder keys, and it has no keyed collection of computed values. A `reactives` collection holds reactives of both kinds in slots that can be added, removed and reordered, and it tracks each slot as a dependency of its own.

At the console, reading a reactive fails unless the read is wrapped in `isolate()`. The examples below turn on shiny's reactive console, which lifts that requirement, so that they read as they would inside an app:

```{r setup}
library(shiny)
library(reactives)

reactiveConsole(TRUE)
```

## Slots hold reactives

A collection holds reactives, such as a `reactiveVal()` for a stored value or a `reactive()` for a computed one, each in a slot of its own. It has two levels, as a list does: the slots, and the values their reactives return. Reading with `$` or `[[` returns a slot's value, as on a `reactiveValues()` object, while reading with `[` returns the slot's reactive:

```{r}
a <- reactiveVal(1)
x <- reactives(a = a, b = reactive(2 * 21))

x$a
x[["b"]]
identical(x["a"], a)
```

Assigning a reactive with `[<-` binds it, adding the slot or replacing its reactive, and assigning `NULL` with `[<-` removes the slot, as for a list:

```{r}
x["c"] <- reactive(x$a + 1)
x["b"] <- NULL
names(x)
```

Assigning with `$<-` or `[[<-` writes a value instead, as on a `reactiveValues()` object. Where the slot holds a `reactiveVal()`, the value is written through it, so the slot keeps its reactive and code holding that `reactiveVal()` sees the new value. Where there is no slot, a new `reactiveVal()` holding the value is bound to it:

```{r}
x$a <- 10
a()
x$c

x$d <- "new"
x$d
```

Unlike on a list, assigning `NULL` with `$<-` or `[[<-` stores `NULL` rather than removing the slot, which only `[<-` does. A slot that stores `NULL` reads as `NULL` with `$`, as a missing slot does, but `[` tells the two apart, since the slot still has a reactive:

```{r}
x$d <- NULL
names(x)
is.null(x$d)
is.null(x["d"])
is.null(x["e"])
```

A slot holding a computed reactive can't be written, so that a stray value can't overwrite it. Replacing its reactive takes `[<-`:

```{r, error = TRUE}
x$c <- 5
```

For a collection of stored values, `reactive_vals()` wraps each value it is given in a `reactiveVal()`. Such a collection only ever holds `reactiveVal()` slots, so each of its slots can be written, and code moving to it from a `reactiveValues()` object keeps its reads and writes with `$` and `[[`. The `as_values()` function returns the value of every slot, as `reactiveValuesToList()` does for `reactiveValues()`:

```{r}
y <- reactive_vals(count = 1, label = "one")
y$count <- 2
as_values(y)
```

For a collection of computed values, `reactive_exprs()` is the counterpart. It only accepts reactive expressions, such as those that `reactive()`, `eventReactive()` or `debounce()` return, so none of its slots can be written. Assigning a value with `$<-` or `[[<-` is an error even where there is no slot, which keeps a stray value from adding one:

```{r, error = TRUE}
w <- reactive_exprs(double = reactive(2 * y$count))
w$double

w$triple <- 6
names(w)
```

## A collection is shared

A collection is shared, as a `reactiveValues()` object is: after `z <- y`, both names refer to the same collection, and a change made through either is seen by everything holding it. That is what lets one part of an app change a collection while another part reacts to it. It is also why `reorder()` changes the collection it is given, rather than returning a reordered copy:

```{r}
z <- y
reorder(z, c("label", "count"))
names(y)
```

## Each slot is a dependency of its own

Reading a slot makes the reader depend on that slot alone: the reader re-runs when the slot is bound, replaced or removed, but not when another slot changes. Reading the slot's value with `$` or `[[` also makes the reader depend on the value, as calling the slot's reactive does. A `reactive()` that reports each computation shows this, since it only recomputes once something it read has changed:

```{r}
x <- reactive_vals(a = 1, b = 2)

a_doubled <- reactive(
  {
    message("Computing a_doubled")
    2 * x$a
  }
)

a_doubled()

x$b <- 3
x$c <- 4
reorder(x, c("c", "b", "a"))
a_doubled()

x$a <- 5
a_doubled()
```

A reader of a slot that doesn't exist yet gets `NULL`, and it re-runs once the slot is added. Once a slot is removed, its readers re-run and get `NULL` in turn.

Testing whether a slot exists by looking for its name in `names(x)` would re-run the reader whenever a slot is added, removed or moved. The `has_key()` function tests for slots with the same dependencies as reading their reactives with `[`:

```{r}
has_key(x, c("a", "d"))
```

Other reads depend on what they return. The `length()` method depends on the number of slots alone, `names()` on which slots exist and their order, and `as_values()` on all of that and on every slot and its value. A reader that lists the slots, for example to offer them as choices, re-runs when a slot is added, removed or moved, but not when a value changes:

```{r}
slot_names <- reactive(
  {
    message("Reading the names")
    names(x)
  }
)

slot_names()

x$a <- 6
slot_names()

x["b"] <- NULL
slot_names()
```

See the Dependencies section of `?reactives` for reads by position.

## In an app

The app below lets the user add terms and remove them again, and it shows their total. Each term is a module, and each module binds a reactive returning its term's value to the collection that the app passes it. Since a collection is shared, the app sees every slot a module binds or removes. The terms are all computed, so the app keeps them in a `reactive_exprs()` collection, where a stray write such as `terms$extra <- 1` fails rather than adding a term to the total.

```{r}
term_ui <- function(id) {

  ns <- NS(id)

  div(
    id = ns("term"),
    numericInput(ns("value"), id, value = 1),
    actionButton(ns("remove"), "Remove")
  )
}

term_server <- function(id, terms) {
  moduleServer(
    id,
    function(input, output, session) {

      terms[id] <- reactive(input$value)
      session$onDestroy(function() terms[id] <- NULL)

      observeEvent(
        input$remove,
        {
          removeUI(paste0("#", session$ns("term")))
          session$destroy()
        }
      )
    }
  )
}

ui <- fluidPage(
  actionButton("add", "Add a term"),
  div(id = "terms"),
  textOutput("total")
)

server <- function(input, output, session) {

  terms <- reactive_exprs()

  observeEvent(
    input$add,
    {
      id <- paste0("term_", input$add)
      insertUI("#terms", ui = term_ui(id))
      term_server(id, terms)
    }
  )

  output$total <- renderText(
    paste("Total:", sum(unlist(as_values(terms))))
  )
}
```

```{r, eval = FALSE}
shinyApp(ui, server)
```

The total reads every slot's value, so it re-runs whenever a term is added, removed or changed, while a reader of `names(terms)` would re-run only when a term is added or removed.

A collection belongs to the session or module it was created in, here the app's session, and is destroyed along with it. Changing it from a module doesn't tie it to that module. A reactive bound to a slot, however, belongs to the module that created it and is destroyed with that module, after which calling it fails. That is why each module removes its slot in `session$onDestroy()`, which runs however the module is destroyed, so that no reader of the collection calls a destroyed reactive. By contrast, in a collection that can be written, the `reactiveVal()` that `$<-` or `[[<-` creates for a new slot belongs to the collection, whichever module writes the value.

## Testing

Inside `testServer()`, the test code sees the server's collection and outputs, so an app like the one above can be checked without a browser. The collection belongs to the session that `testServer()` creates, though, and can't be read once the test is over. To check it afterwards, take a snapshot with `snapshot_reactives()` while the session is still open. A snapshot belongs to no session, and each of its slots returns the value the original slot had when the snapshot was taken:

```{r}
snap <- NULL

testServer(
  server,
  {
    session$setInputs(add = 1)
    session$setInputs(add = 2)
    session$setInputs(`term_1-value` = 3, `term_2-value` = 4)
    session$setInputs(`term_1-remove` = 1)
    print(output$total)
    snap <<- snapshot_reactives(terms)
  }
)

as_values(snap)
```

```{r, include = FALSE}
reactiveConsole(FALSE)
```
