Topic guide: React in Shiny

With shiny.fluent we strived to build an interface for Microsoft's Fluent UI which would be straightforward and natural to use in Shiny. However React, the underlying JavaScript technology, works in a vastly different manner, so the abstractions aren't perfect. Here are a few things you need to know in order to effectively work with shiny.fluent.

Fluent inputs

Shiny has a notion of "inputs", which does not exist in React. Still, you can connect React components to Shiny using appropriate arguments - in particular event handlers and default values. For example, you can use Checkbox in the following way:

shinyApp(
  ui = tagList(
    Checkbox(onChange = setInput("checkbox", 2), defaultChecked = TRUE, label = "Checkbox"),
    textOutput("value")
  ),
  server = function(input, output) {
    output$value <- renderText(deparse(input$checkbox))
  }
)

With setInput("checkbox", 2) we create a JavaScript function which takes its second argument and sends it to Shiny as input$checkbox. The same effect could be achieved with JS("(e, value) => Shiny.setInputValue('checkbox', value)").

The React interface is powerful, but somewhat inconvenient. To make it easier to use components as Shiny inputs, we provide wrappers with .shinyInput suffix. They provide an interface analogous to vanilla Shiny inputs: inputId and optional value. The checkbox usage above can be translated to:

Checkbox.shinyInput("checkbox", value = TRUE, label = "Checkbox")

Additionally, the wrapper:

Dynamic rendering

When you need to dynamically render UI, most of the time you can just go with uiOutput and renderUI. However in some cases you might want to use the analogous ?reactOutput and ?renderReact, which preserve the state of components on rerenders. To understand what that means, consider the following example:

shinyApp(
  ui = div(
    Checkbox.shinyInput("disabled", label = "Disable"),
    reactOutput("ui")
  ),
  server = function(input, output) {
    output$ui <- renderReact({
      TextField.shinyInput("text", label = "TextField", disabled = input$disabled)
    })
  }
)

This app displays a checkbox and a text field. The text field is rendered dynamically, and whether it is enabled or not depends on the value of the checkbox.

If we input some text into the text field and toggle the checkbox, the text field will become disabled, but its contents will be preserved. If we replaced reactOutput and renderReact with uiOutput and renderUI, the contents would be cleared each time we toggled the checkbox.