What's the correct way to structure a Shiny project with a downloadable RMarkdown report?

[Cross-posting from SO:]
I have a reasonably complex Shiny App (several modules) that allows users to fiddle with many different parameters and generate a lot of plots. I want to add the ability for users to download the output they've generated in the Shiny App as a reproducible RMarkdown.

I have read through Shiny - Generating downloadable reports and understand how to do this with a separate Rmarkdown file. However, all of my plotting/data manipulation code is in the Shiny app and it seems like poor practice (violation of DRY) to copy it over to an RMarkdown file -- then any time I want to change the plotting code I would need to change it twice.

What's the correct way to do this so that I don't have to maintain two sets of the same code?

refactor your plotting code

output$plotname <- renderPlot({
... lots of plotting code ...
}}

-->>

plotname_content <- reactive({
... lots of plotting code ...
})
output$plotname <- renderPlot({
     req(plotname_content())
}}

Thereafter you can continue to use the proposed method of generating downloadable results, but you pass plotname_content as a parameter to your rmarkdown

1 Like

@nirgrahamuk that's helpful, thanks. One small issue with that is that my plot is the result of a promise:
This is essentially my server.R:

plotname_content <- reactive({
  future_promise({
     data %>% 
        slow_transform()
  }) %...>% {
    plot_data(.)
  }
})
output$plotname <- renderPlot({
     req(plotname_content())
}}

But then the generated rmarkdown looks like:

Results
Overview
## <Promise [fulfilled: gg]>

There's a lot left out of your post.
I would simply expect that if renderPlot can plot plotname_content() then it could be passed as a parameter. in the equivalent manner to anywhere else.
What happens when you remove the promise layer ? does the markdown work, or give another error ? if the latter then the issue isnt with the promise but with the parameter passing...

Removing the promise works just fine (markdown renders correctly) -- this goes back to my original point: it seems like there's not a great way to share code between Shiny and Markdown. Passing the reactive variable containing the plot seems like a good idea, but is failing in a reasonably common usecase (I imagine that many Shiny apps employ promises)

Perhaps see if this hold function can be used on the markdown side to wait on evaluation of the promise ?

That helped, thanks! I was able to solve my specific issue, but I remain a bit unsatisfied by the larger meta-answer.