Skip to content

Commit

Permalink
Reflow basics chapters and move towards standard style
Browse files Browse the repository at this point in the history
  • Loading branch information
hadley committed May 27, 2019
1 parent cf1a24f commit 8fe6ae8
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 60 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Built with [bookdown](https://bookdown.org/yihui/bookdown/).
1. Uploading/downloading data
2. Interactive base/ggplot2 graphics
3. Generating static reports from Shiny
4. Connecting to databases
5. Modules

4. Mastering UI
Expand All @@ -36,7 +35,8 @@ Built with [bookdown](https://bookdown.org/yihui/bookdown/).

5. Mastering reactivity
1. Reactive programming in depth
2. Using scopes to manage object lifetimes
2. Code organisation; Using scopes to manage object lifetimes
3. Connecting to databases

5. Taming Shiny
1. Troubleshooting and debugging techniques
Expand Down
6 changes: 3 additions & 3 deletions basic-ui.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ source("common.R")

## Introduction

As you saw in the previous chapter, Shiny encourages separation of the code that generates your user interface (`ui`) from the code that drives your app's behavior (`server`). In this chapter, we'll dive deeper into the UI side of things.
Now that you've got a basic app under your belt, we're going to explore the details that make it tick. As you saw in the previous chapter, Shiny encourages separation of the code that generates your user interface (`ui`) from the code that drives your app's behavior (`server`). In this chapter, we'll dive deeper into the UI side of things.

All web pages are constructed out of HTML, and Shiny user interfaces are no different--although they are generally expressed using R, their ultimate job is to produce HTML. As a Shiny app author, you can use high-level layout functions in R that keep you far away from the details of HTML. You can also work with HTML tags directly to achieve any level of customization you want. These approaches are by no means exclusive; you can mix high-level functions with low-level HTML as much as you like.

This chapter will focus on some of the high-level R functions that Shiny provides for input, output, and layout, while the chapter on [Advanced UI][] delves into using lower-level features for authoring HTML directly. If you're new to Shiny, you should read this chapter regardless of your level of HTML knowledge, as inputs and outputs are essential parts of any Shiny app.
This chapter will focus on some of the high-level R functions that Shiny provides for input, output, and layout, while Chapter \@ref(advanced-ui) will delve into using lower-level features for authoring HTML directly.

### Prerequisites {-}

Expand All @@ -31,7 +31,7 @@ library(shiny)

As we saw in the previous chapter, functions like `sliderInput()`, `selectInput()`, `textInput()`, and `numericInput()` are used to insert input controls into your UI.

The first parameter of an input function is always the `inputId`; this is a simple string that is composed of alphanumeric characters and/or underscore (no spaces, dashes, periods, or other special characters allowed!). Most input function have a second parameter, `label`, that is used to create a human-readable label for the control. Any remaining parameters are specific to the particular input function, and can be used to customize the input control.
The first parameter of an input function is always the `inputId`; this is a simple string made up of numbers, letters, and underscores (no spaces, dashes, periods, or other special characters allowed!). Most input function have a second parameter, `label`, that is used to create a human-readable label for the control. Any remaining parameters are specific to the particular input function, and can be used to customize the input control.

For example, a typical call to `sliderInput` might look something like this:

Expand Down
62 changes: 40 additions & 22 deletions reactivity-intro.Rmd
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
# Reactive programming basics
# Reactive programming basics {#reactivity-intro}

```{r}
source("common.R")
```

## Introduction

In the previous chapters, we talked about creating user interfaces. Now we'll move on to discuss the server side of Shiny, where we use R code at runtime to make our user interfaces come alive!
In the the last chapter, we talked about creating user interfaces. Now we'll move on to discuss the server side of Shiny, where we use R code at runtime to make our user interfaces come alive!

In Shiny, you express your server logic using reactive programming. Reactive programming is an elegant and powerful programming paradigm, but it can be disorienting at first for the average R user, who is used to executing code at the R console or in a linearly executed R script.

This chapter will provide a gentle walkthrough of reactive programming, introducing some of the most basic reactive constructs you'll use in your Shiny apps.

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

### Outline {-}

* Section \@ref(server-function) introduces you to the _server function_, the body of which will be the site of most of your reactive programming.
* Section \@ref(input) introduces the `input` object, the humble center of most reactive programs.
* Section \@ref(output) introduces the `output` object, a place to hang logic that populates outputs in the UI.
* Section \@ref(inputs-and-outputs) shows the consequences of accessing `input` values from your `output` logic—the simplest form of reactivity.
* Section \@ref(reactive-exprs) introduces _reactive expressions_, which provide a way to model intermediate computations. Reactive expressions are key to writing Shiny apps that perform well and are easy to reason about.
* Section \@ref(server-function) introduces you to the _server function_, the
body of which will be the site of most of your reactive programming.

* Section \@ref(input) introduces the `input` object, the humble center of most
reactive programs.

* Section \@ref(output) introduces the `output` object, a place to hang logic
that populates outputs in the UI.

* Section \@ref(inputs-and-outputs) shows the consequences of accessing
`input` values from your `output` logic—the simplest form of reactivity.

* Section \@ref(reactive-exprs) introduces _reactive expressions_, which
provide a way to model intermediate computations. Reactive expressions are
key to writing Shiny apps that perform well and are easy to reason about.

## The server function

The typical Shiny app boilerplate looks like this:

```{r eval=FALSE}
```{r, eval = FALSE}
library(shiny)
ui <- fluidPage(
Expand All @@ -36,7 +54,7 @@ shinyApp(ui, server)

As you can see, the `server` function is a function that you (the app author) define, and pass to Shiny via the `shinyApp` function. You'll never invoke a server function yourself; rather, Shiny invokes it whenever a new session begins. (A session begins each time the Shiny app is loaded by a browser.) You can think of the server function as the logic for _initializing_ a new session.

Almost all of the reactive programming you'll do in Shiny will be directly in the server function, at least at first. This is because each time the server function is executed, it creates a private scope for that particular session. Novice Shiny app authors often wonder whether it's safe for two users to connect to a single Shiny app at the same timehow does Shiny keep track of whose inputs and outputs are whose? It's this private scoping that makes it possible, even natural, to maintain separation between sessions.
Almost all of the reactive programming you'll do in Shiny will be directly in the server function, at least at first. This is because each time the server function is executed, it creates a private scope for that particular session. Novice Shiny app authors often wonder whether it's safe for two users to connect to a single Shiny app at the same time: how does Shiny keep track of whose inputs and outputs are whose? It's this private scoping that makes it possible, even natural, to maintain separation between sessions.

Every local variable you create in the body of the server function will be private, accessible only to a particular session. Most of the time, we'll also want our reactive interactions to be private to each session, too: when user A moves a slider, you usually want outputs to update ony for user A, not user B. Therefore, we'll write our reactive logic in the server function.

Expand All @@ -50,15 +68,15 @@ For the moment, we'll focus on `input` and `output`, and leave `session` for lat

The `input` object is a list-like object that contains all the input data sent from the browser, stored by input ID. If your UI contains a numeric input control with an input ID of `count`:

```{r eval=FALSE}
```{r, eval = FALSE}
numericInput("count", label = "Number of values", value = 100)
```

then `input$count` will initially contain the value `100`. As the user changes the value in the browser, `input$count` will be updated automatically by Shiny.

Unlike a typical R list, `input` objects are read-only. The following line will not work:

```{r eval=FALSE}
```{r, eval = FALSE}
# Throws an error
input$count <- 10
```
Expand All @@ -67,7 +85,7 @@ The reason for this restriction is that `input` is supposed to reflect what's ha

One more important thing about `input` is that it's a bit selective about who is allowed to read it. This server function will throw an error:

```{r eval=FALSE}
```{r, eval = FALSE}
server <- function(input, output, session) {
message("The value of input$count is ", input$count)
}
Expand All @@ -77,11 +95,11 @@ To new Shiny app authors, this is an extremely surprising limitation! But rest a

## Output {#output}

Shiny UIs not only contain inputs, but outputs as well. The various `*Output` UI functions—`plotOutput`, `tableOutput`, `verbatimTextOutput`—merely insert empty boxes, or placeholders, into your UI. The `output` object is how you provide Shiny with instructions for populating these empty boxes.
Shiny UIs not only contain inputs, but also outputs. The various `*Output` UI functions—`plotOutput()`, `tableOutput()`, `verbatimTextOutput()`—merely insert empty boxes, or placeholders, into your UI. The `output` object is how you tell Shiny how to populate these empty boxes.

You use the `output` object by wrapping a code block with a render function, then assigning all of that to a slot on the `output` object. Like this:

```{r eval=FALSE}
```{r, eval = FALSE}
server <- function(input, output, session) {
output$hist_plot <- renderPlot({
# Choose 100 random values from a normal distribution,
Expand All @@ -93,9 +111,8 @@ server <- function(input, output, session) {

This snippet assumes that the UI contains a `plotOutput("hist_plot")`. The render function you use (`renderPlot`) must match with the output function (`plotOutput`), and the output slot (`hist_plot`) must match with the output ID.

### Imperative vs. declarative

TODO: Make this a sidebar (somehow)?
::: sidebar
### Imperative vs. declarative {-}

You might read this code and think it means, "Render the plot `hist(rnorm(100L))`, and fill `plotOutput("hist_plot")` with it." But this intuition is wrong, in a subtle but important way. This code does not _instruct_ Shiny to render the plot and send it to the browser, but instead, tells Shiny _how it could_ render the plot, if and when Shiny wanted to. This is the difference between _imperative_ programming, which is what we're used to, and _declarative_ programming, which is what Shiny demands.

Expand All @@ -111,11 +128,13 @@ The correct, declarative reading of the code snippet above is "Whenever you (Shi

(On the other hand, the *contents* of the output's code block—i.e. the rendering logic you supply—may be pretty imperative. It's specifically the assignment of the render function to `output$hist_plot` that's declarative.)

:::

## Using inputs from outputs {#inputs-and-outputs}

The above rendering logic, `hist(rnorm(100))`, isn't very exciting; the user can't interact with this app in any way (unless you count reloading the browser, which would generate a new batch of values). Let's modify the output to use the input. The complete app is below:

```{r eval=FALSE}
```{r, eval = FALSE}
library(shiny)
ui <- fluidPage(
Expand All @@ -138,7 +157,7 @@ Run this app, and you'll notice that as you change the `count` input, the output

Let's see what happens if we add a few more inputs and outputs. In this next example, we've added a new input that affects the histogram: `input$bins` is used to suggest a number of bins to the `hist()` function. We've also added a table output, that limits itself to displaying the first `input$rows` of the data.

```{r eval=FALSE}
```{r, eval = FALSE}
library(shiny)
ui <- fluidPage(
Expand Down Expand Up @@ -166,18 +185,17 @@ server <- function(input, output, session) {
}
shinyApp(ui, server)
```

By careful inspection of the code, we can see that `input$count` affects both outputs, while `input$bins` affects only the plot and `input$rows` affects only the table. Run the app, and you'll see that Shiny knows this too. If you change `input$bins`, the plot is affected but the table is not. If you change `input$rows`, the table is affected but the plot is not.

The point of this example is to demonstrate that Shiny doesn't simply re-render all of its outputs whenever an input has changed. Somehow, without a lot of help from us, Shiny figures out which inputs should affect which outputs. This is the most "magical" aspect of reactive programming, and we'll eventually pull back the curtain in section TODO; for now, just trust that it works.
The point of this example is to demonstrate that Shiny doesn't simply re-render all of its outputs whenever an input has changed. Somehow, without a lot of help from us, Shiny figures out which inputs should affect which outputs. This is the most "magical" aspect of reactive programming, and we'll eventually pull back the curtain; for now, just trust that it works.

### Outputs are atomic

Shiny is smart enough to discern that an output needs to be updated. However, it's not nearly smart enough to know if it can get away with running only part of the output's code block. For example, the code for `hist_plot` has two lines:

```{r eval=FALSE}
```{r, eval = FALSE}
values <- rnorm(input$count)
hist(values, breaks = input$bins)
```
Expand Down
22 changes: 22 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,25 @@ pre {
pre code {
white-space: inherit;
}


/* Sidebar formating --------------------------------------------*/

div.sidebar {
border-left: 7px solid #ccc;
margin: 1em 0;
padding-left: 1em;
}

/* .book .book-body .page-wrapper .page-inner section.normal is needed
to override the styles produced by gitbook, which are ridiculously
overspecified. Goal of the selectors is to ensure internal "margins"
controlled only by padding of container */

.book .book-body .page-wrapper .page-inner section.normal div.sidebar > :first-child {
margin-top: 0;
}

.book .book-body .page-wrapper .page-inner section.normal div.sidebar > :last-child {
margin-bottom: 0;
}
Loading

0 comments on commit 8fe6ae8

Please sign in to comment.