Debug

Published

2024-12-26

Warning

The contents for section are under development. Thank you for your patience.

Expand the callout boxes below to review what we’ve covered in the last five chapters:

In the Documentation chapter we covered:

  1. An introduction to the roxygen2 (5.1 roxygen2 intro) package, and

  2. Some tips for documenting app-package functions (5.2 Documenting app functions)

The applications in Chapter 5 (Documentation) can be accessed with the launch() or get() functions from the shinypak R package:

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

Chapter 1 applications:

list_apps(regex = '^05')
## # A tibble: 1 × 2
##   branch      last_updated       
##   <chr>       <dttm>             
## 1 05_roxygen2 2024-09-03 22:06:19

In Dependencies, we walked through:

  1. How to give users of our app-package access to it’s functions (i.e., exports (6.1 Package exports)), and

  2. What to do with the functions we use from other packages in the R/ folder (i.e., imports (6.2 Package imports))

The applications in Chapter 6 (Dependencies) can be accessed with the launch() or get() functions from the shinypak R package:

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

Chapter 6 applications:

list_apps(regex = '^06')
## # A tibble: 2 × 2
##   branch           last_updated       
##   <chr>            <dttm>             
## 1 06.1_pkg-exports 2024-09-03 22:10:17
## 2 06.2_pkg-imports 2024-09-04 21:31:54

Data covered the three common locations for data in R packages:

  1. data/ (7.1 The data/ folder),

  2. data-raw/ (7.3 The data-raw/ folder), and

  3. inst/extdata/ (7.4 inst/extdata/)

The applications in Chapter 7 (Data) can be accessed with the launch() or get() functions from the shinypak R package:

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

Chapter 7 applications:

list_apps(regex = '^07')
## # A tibble: 1 × 2
##   branch  last_updated       
##   <chr>   <dttm>             
## 1 07_data 2024-09-03 22:14:49

Launch described the differences between

  1. shinyApp() (8.2.1 shinyApp()),

  2. runApp() (8.2.3 runApp()), and

  3. shinyAppDir() (8.2.2 shinyAppDir())

This chapter also provided some options to include in app.R (8.4 The app.R file) and the standalone app function (8.3.3 An updated launch_app()).

The applications in Chapter 8 (Launch) can be accessed with the launch() or get() functions from the shinypak R package:

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

Chapter 8 applications:

list_apps(regex = '^08')
## # A tibble: 1 × 2
##   branch        last_updated       
##   <chr>         <dttm>             
## 1 08_launch-app 2024-12-05 14:41:41

Resources covered how to include external files and/or resources in your app (i.e., those previously stored in www/):

  1. Combining system.file() (9.1.1 system.file()) and addResourcePath() (9.2.1 addResourcePath()), and

  2. The various uses of the inst/ folder (9  Resources)

    1. Alternative images and layout options (9.1.1 system.file())

    2. Development versions of your application using alternative data and modules (9.3 Data files)

    3. A production version of your application (9.5 Production)

The applications in Chapter 9 (External files) can be accessed with the launch() or get() functions from the shinypak R package:

# install.packages('pak')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

Chapter 9 applications:

list_apps(regex = '^09')
## # A tibble: 4 × 2
##   branch                last_updated       
##   <chr>                 <dttm>             
## 1 09.1_inst-www         2024-12-11 22:53:01
## 2 09.2_inst-tidy-movies 2024-12-11 23:55:46
## 3 09.3-inst-quarto      2024-12-11 23:58:11
## 4 09.4_inst-prod        2024-12-12 00:33:55

Debugging is the process of identifying, analyzing, and fixing errors in our code.1 In Shiny apps, debugging can be challenging because the reactive model involves dynamic interactions and a non-linear flow of execution between inputs, reactive expressions, and outputs. Consider the diagram below that illustrates the reactive flow for the scatter plot graph in our application:

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'monospace', 'primaryColor': '#33a02c', 'edgeLabelBackground':'#02577A'}}}%%
flowchart
    subgraph Inputs["Inputs"]
        A1["Variables 
           & Aesthetics"]
    end
    subgraph React["Reactives"]
        R1["<code>var_inputs()</code>"]
        R2["<code>inputs()</code>"]
    end
    subgraph Output["Output"]
        O1[Graph]
    end
    Inputs --> React
    R1 --> R2
    React --> Output

    style Inputs fill:#FFFFFF,color:#000000,stroke:#333,stroke-width:1px,rx:10,ry:10
    style A1 fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style React fill:#FFFFFF,color:#000000,stroke:#333,stroke-width:1px,rx:5,ry:5
    style R1 fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style R2 fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5
    style Output fill:#FFFFFF,color:#000000,stroke:#333,stroke-width:1px,rx:5,ry:5
    style O1 fill:#4CBB9D,color:#FFFFFF,rx:5,ry:5

Debugging

The inputs are collected by the variable input module2 (as var_inputs()) and passed to the scatter display module3, where they become the inputs() for our graph utility function.4

Our application launches with pre-selected values for the x, y and color graph inputs, along with values for the size and opacity (alpha) of the points. Users can change the graph inputs or add a title, but by having pre-selected values, we guarantee the graph renders when the application launches.

The new title is passed with var_inputs() to inputs() and updates the graph to display the text.

The new title is passed with var_inputs() to inputs() and updates the graph to display the text.
%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'monospace'}}}%%
flowchart TD
    subgraph Inputs["Inputs"]
        VarInput[Variables/aesthetics]
        TitleInput["'new plot title'"]
        
    end
    subgraph React["Reactives"]
        R1["<code>var_inputs()</code> "]
        R2["<code>inputs()</code>"]
        R3["Updates <code>var_inputs</code>"]
        R4["Adds  title with <code>labs()</code>"]
    end
    subgraph Output["Output"]
        O1[Graph]
    end
    VarInput --> R1
    TitleInput <--> R3
    R1 --> R2
    R2 <--> R4
    R3 <--> R2
    R2 --> O1
    R4 <--> O1

    style Inputs fill:#FFFFFF,stroke:#333,stroke-width:1px,rx:5,ry:5
    style React fill:#FFFFFF,stroke:#333,stroke-width:1px,rx:5,ry:5
    style Output fill:#FFFFFF,stroke:#333,stroke-width:1px,rx:5,ry:5
    style VarInput fill:#4CBB9D
    style TitleInput fill:#4CB7DB
    style R1 fill:#4CBB9D
    style R2 fill:#4CBB9D
    style R3 fill:#4CB7DB
    style R4 fill:#4CB7DB
    style O1 fill:#4CBB9D

Updating the plot title

Shiny is inherently asynchronous, so bugs can originate from reactive dependencies, execution order, utility functions, hidden states, lazy evaluation, and invalidation (i.e., reactive(), observe(), isolate(), etc.).

For example, we could have an incorrectly defined inputIds or outputIds in the UI, which causes our graph utility function to fail. We could also have missing parentheses on reactive values in the server. Finally, the bug could be originating from the output/render point.

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'monospace'}}}%%
flowchart TD
    subgraph Inputs["Inputs"]
        VarInput[Variables/aesthetics]
        TitleInput["'new plot title'"]
        
    end
    subgraph React["Reactives"]
        R1["<code>var_inputs()</code>"]
        R2["<code>inputs()</code>"]
        R3["Updates <code>var_inputs</code>"]
        R4["Adds title with <code>labs()</code>"]
        B1["🪲<code>inputId()</code> or <code>outputId()</code>🪲"]
        B2["🪲<code>scatter_plot()</code> function🪲"]
        B3["🪲<code>observe()</code> listener🪲"]
        B3["🪲<code>observe()</code> listener🪲"]
        B4["🪲Parentheses <code>()</code> listener🪲"]
    end
    subgraph Output["Output"]
        O1[Graph]
        B5["🪲<code>render_*</code> functions🪲"]
    end
    VarInput --> R1
    Inputs --> B1 --> R1 
    Inputs --> B1 --> R3
    TitleInput <--> R3
    R1 --> R2
    R2 --> B2
    B2 --> R4
    R3 --> B3
    B3 --> R2
    R2 --> B2
    B2 --> O1
    R4 --> B4
    B2 --> B5
    B5 --> O1

    style Inputs fill:#FFFFFF,stroke:#333,stroke-width:1px,rx:5,ry:5
    style React fill:#FFFFFF,stroke:#333,stroke-width:1px,rx:5,ry:5
    style Output fill:#FFFFFF,stroke:#333,stroke-width:1px,rx:5,ry:5
    style VarInput fill:#4CBB9D
    style TitleInput fill:#4CB7DB
    style B1 fill:#FFFFFF,color:#931314,stroke:#931314,stroke-width:3px,rx:10,ry:10
    style B2 fill:#FFFFFF,color:#931314,stroke:#931314,stroke-width:3px,rx:10,ry:10
    style B3 fill:#FFFFFF,color:#931314,stroke:#931314,stroke-width:3px,rx:10,ry:10
    style B4 fill:#FFFFFF,color:#931314,stroke:#931314,stroke-width:3px,rx:10,ry:10
    style B5 fill:#FFFFFF,color:#931314,stroke:#931314,stroke-width:3px,rx:10,ry:10
    style R1 fill:#4CBB9D
    style R2 fill:#4CBB9D
    style R3 fill:#4CB7DB
    style R4 fill:#4CB7DB
    style O1 fill:#4CBB9D

Possible locations of bugs in a reactive model

Understanding the interplay of reactivity, lazy evaluation, asynchronous execution, and hidden states will help us diagnose and resolve bugs in our Shiny app-packages. We have to employ specific strategies and tools to handle Shiny bugs effectively, and some of the traditional debugging approaches can fall short in reactive contexts. The chapters in this section will cover how to adapt common methods for debugging R code to use with our Shiny application.


  1. The definition in this article from AWS gives a great general overview of debugging.↩︎

  2. Our variable input model is in R/mod_var_input.R.↩︎

  3. The graph display module is in R/mod_scatter_display.R.↩︎

  4. The graph utility function is in R/scatter_plot.R↩︎