Skip to content

vue3 reactive input values #6

Open
@timelyportfolio

Description

@timelyportfolio

I have always wondered with both vue or mobx + Shiny how our workflows/architecture might change if Shiny JavaScript state in Shiny.shinyapp.$inputValues was reactive instead of a plain object. In earlier versions of JavaScript without proxy, this idea is very limited in potential usage since added and deleted object properties are not tracked. However, with proxy and the newest versions of mobx and vue, we can track added or effectively replace Shiny.shinyapp.$inputValues with a reactive version early in the session llife and reap the full benefits of JavaScript reactivity fairly cleanly.

Questions

  1. I have not tested real-life usage with large complex apps, but in theory it seems there is no impact. Am I missing something fundamental that would prove this idea is not as feasible as it seems.
  2. I doubt Shiny proper would ever pursue a reactive JS state since it would have to choose which reactive state engine it would use. Is there potential for Shiny to make the choice based on active community and well-tested, stable JS dependencies for Shiny to have a reactive JS engine as its input state? Which library would most likely meet the requirements? I would think choosing an existing library would be better than developing one from scratch.
  3. I feel like the concept of reactivity is quickly engrained in most Shiny developers and the reactivity concepts would translate easily to JavaScript. What would get in the way of quick understanding for R Shiny developers? What might be intimidating or difficult?
  4. How might we best communicate the improvement from incorporating reactive JS input state?
  5. What tools/tooling could we provide to ease the integration?

Code

library(htmltools)
library(vueR)
library(shiny)

# experiment with standalone vue reactivity in bare page
#   reference:
#     https://vuejs.org/v2/guide/reactivity.html
#     https://dev.to/jinjiang/understanding-reactivity-in-vue-3-0-1jni
browsable(
  tagList(
    tags$head(
      tags$script(src = "https://unpkg.com/@vue/[email protected]/dist/reactivity.global.js"),
    ),
    tags$p("we should see a number starting at 0 and increasing by one each second"),
    tags$div(id = "reporter"),
    tags$script(HTML(
"
let data = {x: 0};
let data_reactive = VueReactivity.reactive(data)  // could also use ref for primitive value
console.log(data, data_reactive)

VueReactivity.effect(() => {
  console.log(data_reactive.x)
  document.getElementById('reporter').innerText = data_reactive.x
})
setInterval(function() {data_reactive.x++}, 1000)

"
    ))
  )
)


# experiment with Shiny inputValues and vue-next
#   reference:
#     https://vuejs.org/v2/guide/reactivity.html
#     https://dev.to/jinjiang/understanding-reactivity-in-vue-3-0-1jni
ui <- tagList(
  tags$head(
    tags$script(src = "https://unpkg.com/@vue/[email protected]/dist/reactivity.global.js"),
  ),
  tags$div(
    tags$h3("Increment with JavaScript"),
    tags$span("Shiny: "),
    textOutput("reporterR", inline = TRUE),
    tags$span("JavaScript: "),
    tags$span(
      id = "reporterJS"
    )
  ),
  tags$div(
    tags$h3("Increment with R/Shiny"),
    tags$span("Shiny (used numeric input for convenience): "),
    numericInput(inputId = 'x2', label = "", value = 0),
    tags$span("JavaScript: "),
    tags$span(
      id = "reporterJS2"
    )
  ),
  tags$script(HTML(
"
$(document).on('shiny:connected', function() {

  // once Shiny connected replace Shiny inputValues with reactive Shiny inputValues
  Shiny.shinyapp.$inputValues = VueReactivity.reactive(Shiny.shinyapp.$inputValues)

  // do our counter using Shiny.setInputValue from JavaScript
  Shiny.setInputValue('x', 0) // initialize with 0
  VueReactivity.effect(() => {
    console.log('javascript', Shiny.shinyapp.$inputValues.x)
    document.getElementById('reporterJS').innerText = Shiny.shinyapp.$inputValues.x
  })
  setInterval(
    function() {
      Shiny.setInputValue('x', Shiny.shinyapp.$inputValues.x + 1) //increment by 1
    },
    1000
  )

  // react to counter implemented in Shiny
  VueReactivity.effect(() => {
    console.log('shiny', Shiny.shinyapp.$inputValues['x2:shiny.number'])
    document.getElementById('reporterJS2').innerText = Shiny.shinyapp.$inputValues['x2:shiny.number']
  })

})
"
  ))
)

server <- function(input, output, session) {
  x2 <- 0  # use this for state of Shiny counter
  output$reporterR <- renderText({input$x})

  observe({
    invalidateLater(1000, session = session)
    x2 <<- x2 + 1 # <<- or assign required to update parent
    updateNumericInput(inputId = "x2", value = x2, session = session)
  })
}

shinyApp(
  ui = ui,
  server = server,
  options = list(launch.browser = rstudioapi::viewer)
)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions