|
| 1 | +--- |
| 2 | +title: "Authoring inputs powered by React with reactR" |
| 3 | +author: |
| 4 | + - Alan Dipert |
| 5 | +date: "`r Sys.Date()`" |
| 6 | +output: rmarkdown::html_vignette |
| 7 | +vignette: > |
| 8 | + %\VignetteIndexEntry{Shiny inputs with reactR} |
| 9 | + %\VignetteEngine{knitr::rmarkdown} |
| 10 | + %\VignetteEncoding{UTF-8} |
| 11 | +--- |
| 12 | + |
| 13 | +```{r, echo=FALSE, include=FALSE} |
| 14 | +knitr::opts_chunk$set(eval = FALSE) |
| 15 | +``` |
| 16 | + |
| 17 | +[Shiny](http://shiny.rstudio.com/) comes with a large library of input |
| 18 | +[widgets](https://shiny.rstudio.com/gallery/widget-gallery.html) for collecting |
| 19 | +input from the user and conveying input data to R. |
| 20 | + |
| 21 | +If you want a kind of input *not* provided by Shiny — like a color picker, |
| 22 | +or a different kind of slider — you've always been able to build your own. |
| 23 | +Shiny's input system is |
| 24 | +[extensible](https://shiny.rstudio.com/articles/building-inputs.html). All |
| 25 | +that's required is an understanding of certain conventions and a little custom |
| 26 | +JavaScript. |
| 27 | + |
| 28 | +reactR provides additional tools to ease the creation of new Shiny inputs |
| 29 | +implemented using React. In the following tutorial, we will demonstrate these |
| 30 | +tools by implementing a new Shiny color picker input that wraps the |
| 31 | +[react-color](https://github.com/casesandberg/react-color) library. |
| 32 | + |
| 33 | +## Software pre-requisites |
| 34 | + |
| 35 | +In order to develop a **reactR** Shiny input, you'll need to install R and |
| 36 | +optionally RStudio. If you're on Windows, you should also install |
| 37 | +[Rtools](https://cran.r-project.org/bin/windows/Rtools/). |
| 38 | + |
| 39 | +> For an excellent general introduction to R package concepts, check out the [R |
| 40 | +> packages](http://r-pkgs.had.co.nz/) online book. |
| 41 | +
|
| 42 | +In addition, you'll need to install the following JavaScript tools on your |
| 43 | +machine: |
| 44 | + |
| 45 | +* [Node.js](https://nodejs.org): JavaScript engine and runtime for development |
| 46 | + outside of browsers. Provides the `node` and `npm` commands. |
| 47 | +* [Yarn](https://yarnpkg.com/en/): Command-line dependency management tool, |
| 48 | + provides the `yarn` command. |
| 49 | + |
| 50 | +To follow along in this vignette, you'll also need the following R packages: |
| 51 | + |
| 52 | +```{r} |
| 53 | +install.packages(c("shiny", "devtools", "usethis", "reactR")) |
| 54 | +``` |
| 55 | + |
| 56 | +## Scaffolding |
| 57 | + |
| 58 | +To create a new widget you can call `scaffoldReactInput` to generate the basic |
| 59 | +structure and build configuration. This function will: |
| 60 | + |
| 61 | +* Create the .R, .js, and .json files required by your input; |
| 62 | +* If provided, take an [npm](https://www.npmjs.com/) package name and version as |
| 63 | + a named list with `name` and `version` elements. For example, the npm package |
| 64 | + `foo` at version `^1.2.0` would be expressed as `list(name = "foo", version = |
| 65 | + "^1.2.0")`. The package, if provided, will be added to the new widget's |
| 66 | + `package.json` as a build dependency. |
| 67 | + |
| 68 | +The following R code will create an R package named **colorpicker**, then |
| 69 | +provide the templating for creating an input powered by the |
| 70 | +`react-color` library on npm: |
| 71 | + |
| 72 | +```{r} |
| 73 | +# Create the R package |
| 74 | +usethis::create_package("~/colorpicker") |
| 75 | +# Inject the widget templating |
| 76 | +withr::with_dir( |
| 77 | + "~/colorpicker", |
| 78 | + reactR::scaffoldReactInput("colorpicker", list("react-color" = "^2.17.0"), edit = FALSE) |
| 79 | +) |
| 80 | +``` |
| 81 | + |
| 82 | +## Building and installing |
| 83 | + |
| 84 | +### Building the JavaScript |
| 85 | + |
| 86 | +The next step is to navigate to the newly-created `sparklines` project and run |
| 87 | +the following commands in the terminal: |
| 88 | + |
| 89 | +``` |
| 90 | +yarn install |
| 91 | +yarn run webpack |
| 92 | +``` |
| 93 | + |
| 94 | +* `yarn install` downloads all of the dependencies listed in `package.json` and |
| 95 | + creates a new file, `yarn.lock`. You should add this file to revision control. |
| 96 | + It will be updated whenever you change dependencies and run `yarn install`. |
| 97 | + **Note: you only need to run it after modifying package.json**. For further |
| 98 | + documentation on `yarn install`, see the [yarn |
| 99 | + documentation](https://yarnpkg.com/lang/en/docs/cli/install/). |
| 100 | + |
| 101 | +* `yarn run webpack` compiles the [ES2015](https://babeljs.io/docs/en/learn/) |
| 102 | + JavaScript source file at `srcjs/colorpicker.jsx` into |
| 103 | + `www/colorpicker/colorpicker/colorpicker.js`. The latter file is the one |
| 104 | + actually used by the R package and includes all the relevant JavaScript |
| 105 | + dependencies in a dialect of JavaScript that most browsers understand. |
| 106 | + |
| 107 | +`yarn run webpack` is not strictly a `yarn` command. In fact, `yarn run` simply |
| 108 | +delegates to the [webpack](https://webpack.js.org/) program. Webpack's |
| 109 | +configuration is generated by `scaffoldReactInput` in the file |
| 110 | +`webpack.config.js`, but you can always change this configuration and/or modify |
| 111 | +the `yarn run webpack` command to suit your needs. |
| 112 | + |
| 113 | +### Installing the R package |
| 114 | + |
| 115 | +Now that the input's JavaScript is compiled, go ahead and install the R |
| 116 | +package: |
| 117 | + |
| 118 | +```{r} |
| 119 | +devtools::document() |
| 120 | +devtools::install(quick = TRUE) |
| 121 | +``` |
| 122 | + |
| 123 | +Alternatively, in RStudio, you can use the keyboard shortcuts `Ctrl+Shift+D` and |
| 124 | +`Ctrl-Shift-B` to document and build the package. (On macOS, the shortcuts are |
| 125 | +`Cmd+Shift+D` and `Cmd+Shift+B`) |
| 126 | + |
| 127 | +## Run the included demo |
| 128 | + |
| 129 | +Now that the input's JavaScript is compiled, and the R package is installed, |
| 130 | +run `app.R` to see a demo in action: |
| 131 | + |
| 132 | +```{r} |
| 133 | +shiny::runApp() |
| 134 | +``` |
| 135 | + |
| 136 | +Alternatively, in RStudio, you can open `app.R` and press `Ctrl-Shift-Enter` |
| 137 | +(`Cmd-Shift-Enter` on macOS). You should see something like the following appear |
| 138 | +in the Viewer pane: |
| 139 | + |
| 140 | + |
| 141 | + |
| 142 | +## Authoring a React input |
| 143 | + |
| 144 | +At this point, we've built some scaffolding for an input powered by React. |
| 145 | +Let's modify it to create an interface to the `react-color` library. |
| 146 | + |
| 147 | +### Connecting Shiny with React |
| 148 | + |
| 149 | +Consider the following example taken from the [react-color |
| 150 | +documentation](http://casesandberg.github.io/react-color/). |
| 151 | + |
| 152 | +```js |
| 153 | +import React from 'react'; |
| 154 | +import { SketchPicker } from 'react-color'; |
| 155 | + |
| 156 | +class Component extends React.Component { |
| 157 | + |
| 158 | + render() { |
| 159 | + return <SketchPicker />; |
| 160 | + } |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +That JavaScript code produces a `SketchPicker`-type interface that looks like |
| 165 | +this: |
| 166 | + |
| 167 | + |
| 168 | + |
| 169 | +However, that example doesn't demonstrate a way to default to a particular |
| 170 | +color, or a way to cause something to happen when the color changes. To |
| 171 | +accomplish these, `react-color` components can [optionally |
| 172 | +take](http://casesandberg.github.io/react-color/#api) the following |
| 173 | +[props](https://reactjs.org/docs/components-and-props.html): |
| 174 | + |
| 175 | +* `color`: accepts a string of a hex color like `'#333'` |
| 176 | +* `onChangeComplete`: accepts a function taking a single argument, the new |
| 177 | + color, that will be called when the new color is selected |
| 178 | + |
| 179 | +These operations are conceptually similar enough to the API expected of Shiny |
| 180 | +inputs that `reactR` can assist with mapping components to inputs. |
| 181 | + |
| 182 | +It does so by introducing a convention for wrapping components like those |
| 183 | +provided by `react-color` with an intermediate component that accepts these |
| 184 | +props: |
| 185 | + |
| 186 | +* `configuration`: A configuration object containing data from R used to |
| 187 | + parameterize the input's behavior |
| 188 | +* `value`: The input's values over time, beginning with the default |
| 189 | +* `setValue`: A function to call with the input's new value when one is created |
| 190 | + |
| 191 | +The `configuration` and `value` props are initially populated on the R side, as |
| 192 | +arguments to the `createReactInput` function inside the input's constructor |
| 193 | +function. In the case of our newly-scaffolded input, that happens in |
| 194 | +`R/colorpicker.R`. |
| 195 | + |
| 196 | +The `setValue` function is what causes new values to be sent to R, and also what |
| 197 | +triggers the "intermediate" component to repaint itself. |
| 198 | + |
| 199 | +So, in order to make the components delivered by `react-color` accessible on the |
| 200 | +R side, we must create our own intermediate component that wraps one of |
| 201 | +`react-color`'s pickers. |
| 202 | + |
| 203 | +### Create intermediate component |
| 204 | + |
| 205 | +Open `srcjs/colorpicker.jsx` and paste the following in: |
| 206 | + |
| 207 | +```js |
| 208 | +import { reactInput } from 'reactR'; |
| 209 | +import { SketchPicker } from 'react-color'; |
| 210 | + |
| 211 | +const PickerInput = ({ configuration, value, setValue }) => { |
| 212 | + return ( |
| 213 | + <SketchPicker |
| 214 | + color={ value } |
| 215 | + onChangeComplete={ color => setValue(color.hex) } |
| 216 | + /> |
| 217 | + ); |
| 218 | +}; |
| 219 | + |
| 220 | +reactInput('.colorpicker', 'colorpicker', PickerInput); |
| 221 | +``` |
| 222 | + |
| 223 | +The above code creates a new [function |
| 224 | +component](https://reactjs.org/docs/components-and-props.html#function-and-class-components) |
| 225 | +called `PickerInput` that expects the props supplied by reactR and renders a |
| 226 | +parameterized `SketchPicker` from `react-color`. The `configuration` value is |
| 227 | +not yet used. |
| 228 | + |
| 229 | +After saving the file, run `yarn run webpack` in the terminal and rebuild the |
| 230 | +package. |
| 231 | + |
| 232 | +## Trying it out |
| 233 | + |
| 234 | +After rebuilding the JavaScript and the package, try running `app.R` again. You |
| 235 | +should see something like this: |
| 236 | + |
| 237 | + |
| 238 | + |
| 239 | +When you select new colors, you should see the `textOutput` update accordingly. |
| 240 | + |
| 241 | +You might have noticed that the input showed up initially without a color |
| 242 | +selected. That's because in `app.R` we didn't supply a `default` argument to the |
| 243 | +`colorpickerInput` function inside our `ui`. |
| 244 | + |
| 245 | +Try replacing the call to `colorpickerInput` with this: |
| 246 | +`colorpickerInput("textInput", default = "#a76161")` |
| 247 | + |
| 248 | +Now when you run the app, the color should start as a shade of red. |
| 249 | + |
| 250 | +## Further learning |
| 251 | + |
| 252 | +This tutorial walked you through the steps taken to wrap the `react-color` |
| 253 | +library in a Shiny input. The full example package is accessible at |
| 254 | +<https://github.com/react-R/colorpicker-example>. Our intention is keep creating |
| 255 | +example packages under the <https://github.com/react-R> organization, so head |
| 256 | +there if you'd like to see other examples of interfacing with React. |
0 commit comments