---
title: "Introduction to shinyreprex"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Introduction to shinyreprex}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

library(shinyreprex)
```

## Using shinyreprex

There is a single exported function, `reprex_reactive``, that takes a reactive object and converts 
it into a script that can be reused outside of the Shiny application to reproduce the result 
of the reactive. This can be sent to a simple `verbatimTextOutput` or something more UX 
friendly such as the `{highlighter}` package to display the script in the UI.

## Best Practices

### Bind `reprex_reactive` call to Event

The code to reproduce a given reactive will be updated whenever an input or reactive feeding
into the provided reactive is updated, therefore it is recommended to have the reactive as
an event attached.

```r
width_range <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  range(iris_filt$Petal.Width)
})

# Good
repro_range <- reactive(reprex_reactive(width_range)) |>
  bindEvent(width_range())
  
# Bad
repro_range <- reactive(reprex_reactive(width_range))
```

### Put Side-Effects in Observers

This is general best-practice when developing Shiny applications, but avoid putting code 
for its side effects in `reactive` expressions, and instead create smaller reactive calls, 
and have observers running the code not intended for reproducing outputs.

```r
# Good
width_range <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  range(iris_filt$Petal.Width)
})

observe({
  updateSliderInput("width", width_range())
})

# Bad
width_range <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  widths <- range(iris_filt$Petal.Width)
  updateSliderInput("width", widths)
  widths
})

summary_data <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  shinyjs::toggle("table", condition = nrow(iris_filt) > 0L)
  dplyr::summarise(iris_filt, dplyr::across(where(is.numeric), mean))
})
```

### Create a Business Logic Package

In order that developers can easily recreate outputs generated in Shiny applications, 
add any business logic, such as ETL, data manipulation and modelling, to a separate package. 
This will allow users to recreate the tables and plots generated in the app without having to
install all the packages associated with the application.

### Use Secrets in Reactives

If you are using secrets, such as environment variables, make sure that they are defined within a reactive
expression. If it is defined in the module, or in the global environment, the secret will be written
in the assignment. 

```r
# Good
moduleServer(id, function(input, output, session) {
  my_reactive <- reactive({
    api_key <- Sys.getenv("MY_API_KEY")
    ...
  })
})

# Bad
moduleServer(id, function(input, output, session) {
  api_key <- Sys.getenv("MY_API_KEY")
  
  my_reactive <- reactive({
    ...
  })
})
```
