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

Unbind React inputs after being bound automatically by Shiny #47

Closed jakubsob closed 1 year ago

jakubsob commented 2 years ago

46

Changes description

Scenarios: static rendering, reactOutput

After an element has been rendered using findAndRenderReactData() the element is unbound.

Questions

It works based on assumption that Shiny binds new component immediately after ReactDOM.render is called. Is it possible that we will unbind input before it has been bound by Shiny (resulting in no effect)?

Scenario: uiOutput

uiOutput itself binds inputs each time inputs are added to DOM. Mutation observer watches changes in nodes those nodes and removes bindings from React inputs.

Questions

Do we want to modify behavior of uiOutput? Maybe we'd like to only use React components using reactOutput and keep uiOutput unchanged as it's the expected behavior there that it binds all inputs within?

Test case

Comment out line 24 in adapters.js to test that React components don't set input values anymore. Without the changes from this PR values are set even without explicit ShinyProxy.setInputValue(inputId, value); call in the useValue effect.

library(shiny)
library(rlang)
library(shiny.react)
library(shiny.fluent)
library(purrr)

shinyApp(
  ui = fluentPage(
    h3("UI"),
    textInput("shiny", label = "Shiny"),
    TextField.shinyInput("fluent", label = "Fluent"),
    textOutput("shiny_value"),
    textOutput("fluent_value"),
    Separator(),
    h3("reactOutput"),
    checkboxInput("show_react", "Show",  TRUE),
    reactOutput("react"),
    textOutput("shiny_react_value"),
    textOutput("fluent_react_value"),
    Separator(),
    h3("uiOutput"),
    checkboxInput("show_ui", "Show",  TRUE),
    uiOutput("ui"),
    textOutput("shiny_ui_value"),
    textOutput("fluent_ui_value")
  ),
  server = function(input, output) {
    updates <- reactiveValues()
    ids <- c("shiny", "fluent", "shiny_react", "fluent_react", "shiny_ui", "fluent_ui")
    walk(ids, ~ observeEvent(input[[.x]], updates[[.x]] <- updates[[.x]] %||% 0 + 1))
    walk(ids, ~ { output[[paste0(.x, "_value")]] <- renderText(paste(.x, updates[[.x]], deparse(input[[.x]]))) })

    output$react <- renderReact({
      if (input$show_react) {
        tagList(
          textInput("shiny_react", label = "Shiny"),
          TextField.shinyInput("fluent_react", label = "Fluent")
        )
      }
    })

    output$ui <- renderUI({
      if (input$show_ui) {
        tagList(
          textInput("shiny_ui", label = "Shiny"),
          TextField.shinyInput("fluent_ui", label = "Fluent")
        )
      }
    })
  }
)

Definition of Done checklist

kamilzyla commented 1 year ago

Closing as superseded by #50.