0

I would like to pass a variable (or tibble/data.frame) to a module function, then receive a variable (or tibble/data.frame) from the module. Then act on the returned variable. So that each time I call the moduleServer with a unique namespace I can pass unique variable and get same the ui/module with different content, then act differently. As appose to using a global reactive variable.

The following code pattern/code block works in some situations (which I would like to keep if i can):

  • Works with passing variables that are static or reactive
  • Works with for tibble/data.frame (tested in a different script) but not for variables (script below).

but its seems very inefficient in how the reactivity managed in the upper module server. It needs to nest the reactivity before passing it to the lower module.

library(shiny)
library(tidyverse)

lower_UI <- function(id) {
  tagList(
    uiOutput(NS(id, "text3")),
    textOutput(NS(id,"verbose3")),
    actionButton(NS(id,"goButton"), label = "beep me", class = "btn-success")
  )
}

lower <- function(id, pass) {
  moduleServer(id, function(input, output, session) {

    lowerV = reactiveValues(returnV = NULL, inV = pass)


    output$text3 <- renderUI(textInput(session$ns("text3"), "test-lower", "test"))
    output$verbose3 <- renderText(paste0(input$text3, " ", lowerV$inV))

    observe({
      lowerV$returnV = paste0("beeped: ", input$text3, " ", lowerV$inV)
    }) %>% bindEvent(input$goButton)



    return(reactive(lowerV$returnV))
})
}

upper_UI <- function(id) {
  tagList(
    uiOutput(NS(id, "text_in")),
    lower_UI(NS(id, "test"))
  )

}

upper <- function(id) {
  moduleServer(id, function(input, output, session) {

    upperV <- reactiveValues(one = NULL, react = NULL)

    static = "static"

    output$text_in <- renderUI(textInput(NS(id, "upper_text"), "test-upper", "reactive"))

    observe({
      upperV$react <- input$upper_text
    }) %>% bindEvent(input$upper_text)

    # pick static or reactive
    upperV$one <- reactive(lower("test", pass = upperV$react))
    #upperV$one <- reactive(lower("test", pass = static))

    observeEvent(upperV$one()(), {
      if(!is.null(upperV$one()())){
        print(upperV$one()())
      }
    })
  })
}

ui <- fluidPage(
  upper_UI("upper")
)

server <- function(input, output, session) {
  upper("upper")
}

shinyApp(ui, server)

the bug: I updated observe to observeEvent(upperV$one()(),, since it should only triggers when the module return value is updated from the button click.

This works for the first button click. However, after that I am getting odd behaviour where input data from upper-test streams to print & lower-test would update print after button click. Instead of only updating print after button click. This does not seem to happen when I am passing a tibble.

side question: is there a better method? Have I missed something obvious? can it be made more efficient?

Why do I have an issue with the efficiency? > I am not sure is it suppose to work this way. It seems overly complex for basic function behaviour. Based on other posts It seems like should be able to pass them as reactive objects.

Aaron C
  • 301
  • 1
  • 8

1 Answers1

0

You are already stating in the lower module that you are passing a reactive object, so you don't need to apply reactive() to the returned object.

The following works just as fine:

 # pick static or reactive
    #upperV$one <- lower("test", pass = upperV$react)
    upperV$one <- lower("test", pass = static)
    
    observe({
      if(!is.null(upperV$one())){
        print(upperV$one())
      }
    })

As for the question is this inefficient, I suppose that comes to the eye of the beholder? I'm not really seeing a problem with the code following the revision I mentioned above.

I'd use observeEvent() or eventReactive() instead of bindEvent(). I also question why there's a need to create a reactiveValues() list object - They could just be reactive objects. But these are quibbles, I don't believe either method really have an impact on the app's performance.

EDIT: As of v1.6, RStudio folks recommend using bindEvent() over observeEvent()/eventReactive(). Italicized comment above should be disregarded.

Phil
  • 7,287
  • 3
  • 36
  • 66
  • Thank you for responding. >> if I remove the reactive() around lower then i can't pass the reactive variable > I get the error "Can't access reactive value 'react' outside of reactive consumer" >> inefficiency, after using the code for a bit.. there is an inefficiency/problem (will update the Question). >> The main reason for using bindEvent() is that Shiny recommends it over eventReactive() or observeEvent(). However, there are situations where I find ObserveEvent() to work when bindEvent() doesn't. So I switch. – Aaron C Aug 29 '23 at 00:06
  • I fixed the inefficiency/problem that i just found. – Aaron C Aug 29 '23 at 01:10
  • "reactiveValues() list object - They could just be reactive objects." >> assuming you mean that I don't need to activate/initialise them. If I don't initialise them I get errors. – Aaron C Aug 29 '23 at 01:30
  • Use `req()` so that they only activate when a condition is truthy. – Phil Aug 29 '23 at 02:29
  • "The main reason for using bindEvent() is that Shiny recommends it over eventReactive() or observeEvent()." Their documentation says it is more flexible, but I don't see any indication that they recommend one over the other. – Phil Aug 29 '23 at 03:00
  • 1
    I can see how ```req()``` could be an alternative for initialling. Which would be useful in certain situations. As for shiny recommendations.. from their observeEvent doc: "As of Shiny 1.6.0, we recommend using bindEvent() instead of eventReactive() and observeEvent()." ... because it is more flexible as you mention.. – Aaron C Aug 29 '23 at 04:11