Appsilon / shiny.react

Use React in Shiny applications.
https://appsilon.github.io/shiny.react
GNU Lesser General Public License v3.0
96 stars 12 forks source link

Delayed rendering #50

Closed kamilzyla closed 1 year ago

kamilzyla commented 1 year ago

Changes

  1. Render React:
    1. Asynchronously (in a separate iteration of the JS event loop) to prevent Shiny from automatically binding inputs / outputs (closes #46).
    2. Only once Shiny is fully initialized to remove the need for ShinyProxy, our previous workaround for Shiny's delayed initialization.
  2. Remove the now unnecessary ShinyProxy.
  3. Apply ShinyBindingWrapper to shiny::actionButton() and shiny.react::reactOutput() so they work in any React context (closes #21). This defect was uncovered by the rendering changes and caused issues with shiny.fluent showcase dashboard and E2E tests, so I decided to fix it in the same PR.
  4. Refactor the code around ShinyBindingWrapper.

How to test

  1. The shiny.fluent CI should pass with shiny.react installed from this branch (R CMD check, E2E test).
  2. The code example from #21 should now work.
  3. The snippets below should now work.

Unwanted bindings

The following snippet should now output character(0) to the R console, indicating that an <input> rendered by React is not bound regardless of the context. It used to output c("static", "render-ui"):

library(shiny.react)

test <- function(id) {
  shiny.react:::ReactContext(
    tags$input(id = id, type = "text")
  )
}

shinyApp(
  ui = tagList(
    uiOutput("ui"),
    reactOutput("react"),
    test("static"),
    shiny.react:::Box(test("dynamic")),
  ),
  server = function(input, output) {
    observe(dput(names(input)))
    output$ui <- renderUI(test("render-ui"))
    output$react <- renderReact(test("render-react"))
  }
)

Nested reactOutput()

The following snippet should now render Yay! (it used to render nothing):

library(shiny.react)

shinyApp(
  ui = reactOutput("outer"),
  server = function(input, output) {
    output$outer <- renderReact(reactOutput("inner"))
    output$inner <- renderReact("Yay!")
  }
)

Action buttons in React context

The button and link should log to R console (they used to do nothing):

library(shiny.react)

shinyApp(
  ui = reactOutput("react"),
  server = function(input, output) {
    output$react <- renderReact({
      tagList(
        actionButton("button", "Button"),
        actionLink("link", "Link")
      )
    })
    observe(dput(input$button))
    observe(dput(input$link))
  }
)

The added binding wrapper causes a difference in layout (the button and link now appear on separate lines; they used to be on one line). We already have that problem with other inputs/outputs (#52); I have a fix which I will submit at a later time.