Super Solutions for Shiny Apps #1: Using Session Data

Reading time:
time
min
By:
Marcin Dubel
September 24, 2019

<h2>TL;DR</h2> The <b><i>session</i></b> argument can help you organize the app’s content. In this article I explain how to use it as a global list for passing parameters between modules in advanced Shiny apps to simplify how objects flow in the code. Managing dependencies between modules with <i>sessions</i> this way is much faster than doing it manually. This is the first in a series of tips and tricks for advanced Shiny developers. The ideas presented in the series will help keep your Shiny apps bug-free, transparent, organized, fast, and reliable. <h2>Introduction</h2> The potential issue of <b>“passing values between modularized applications” </b>does not necessarily come up when writing simple “Hello World” applications. However, most advanced Shiny practitioners eventually run into this problem when creating multi-view, modularized applications. The official RStudio article "<a href="https://shiny.rstudio.com/articles/communicate-bet-modules.html">Communication between modules</a>" offers one solution that works reasonably well - returning the list of all inputs from one module and using them as parameters in subsequent module calls. It does come with certain limitations, so let us consider these downsides first and discuss an alternative solution. <h2>Context &amp; the Problem</h2> Shiny was first designed for simple prototype applications used by data scientists to help in their everyday work. The community quickly discovered, however, that Shiny’s potential is far greater and started to develop advanced dashboards.  This can create significant problems – keeping the entire app structure in just two files, <i>ui</i> and <i>server</i>, makes the development process far less effective as the code base grows in size. Further, once you build a large chunk of the application you might wish to re-use it in your other projects. While Shiny modules do provide for that - developers can now create parts of the code in separate files and combine them into advanced dashboards - there is a tradeoff here. On the one hand, the separate parts need to be interdependent within the app. On the other hand, they have to be independent enough to facilitate their use elsewhere. Balancing this can be very difficult to achieve as an app grows in size and its modules’ dependencies become more complicated. It eventually gets difficult to organize all the traffic between the saved results and parameters passed to modules.  Let’s explore this issue on a real life example of nested modules: there is a screen (1st tier module) with a few tables (a single table is a 2nd tier module) and each table consists of a few columns (a column is a 3rd tier module). There is also a module with filters (e.g. opened on modal) with sections for each table and for a single condition. <img class="aligncenter size-full wp-image-2628" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b022ca3b3143b587a2fe25_dependency-ladder.webp" alt="dependency ladder Shiny app" width="512" height="225" /> The basic Shiny system would require passing the parameters up-and-down the dependency ladder. Imagine that you need to use yet another parameter from <i>single-condition </i>in <i>single-column</i>. Such a simple operation might require quite a lot of changes in parameters passed in each module. While this should work just fine, especially if organized properly, the <i>session</i> solution can be far more efficient and less cumbersome for the developer. Let us dive into the details. <h2>The Solution</h2> As you probably know, the <i>ui </i>part of the module should always contain at least the <i>id </i>parameter and the <i>server</i> part always contains the boilerplate parameters <i>input, output, session </i>(for more information, <a href="https://shiny.rstudio.com/articles/modules.html">this Joe Cheng post</a> provides an introduction to Shiny modules). <i>Session </i>is not required by default but it can be used to save user data. It has to be added as an argument to the main <i>server </i>function as well as to each of the modules. Since each module has access to the <i>session</i> argument, anything that we store there will be universally accessible. Indeed, you can consider it a global list of stored values. You may be familiar with <i>ns <- session$ns, </i>which is the same concept. Now we will use it for passing the other values as well. Let’s explore this idea by modifying the <a href="https://shiny.rstudio.com/articles/communicate-bet-modules.html">example presented by RStudio</a>. In this rather simple app the module <i>scatterplot_server_mod </i>requires three additional parameters (<i>dataset, plot1vars, plot2vars</i>) to be passed to the module call. We will try to simplify it and keep the functionality working without using any external parameters. It might not be strictly necessary in this case, but as we saw in our example above, things can escalate quickly. All we need to do is save the results from the server file to the list stored in the global session rather than to local objects. Let’s turn this: <figure class="highlight"> <pre class="language-r"><code class="language-r" data-lang="r"># server logic server &lt;- function(input, output, session) {  # prepare dataset  ames &lt;- make_ames()  # execute plot variable selection modules  plot1vars &lt;- callModule(varselect_mod_server, "plot1_vars")  plot2vars &lt;- callModule(varselect_mod_server, "plot2_vars")  # execute scatterplot module  res &lt;- callModule(scatterplot_mod_server,                    "plots",                    dataset = ames,                    plot1vars = plot1vars,                    plot2vars = plot2vars) } </code></pre> </figure> Into this: <figure class="highlight"> <pre class="language-r"><code class="language-r" data-lang="r"># server logic server &lt;- function(input, output, session) { <br>  # prepare dataset  session$userData$dataset &lt;- make_ames()  # execute plot variable selection modules  session$userData$plot1vars &lt;- callModule(varselect_mod_server, "plot1_vars")  session$userData$plot2vars &lt;- callModule(varselect_mod_server, "plot2_vars") <br>  res &lt;- callModule(scatterplot_mod_server, "plots") } </code></pre> </figure> We can now use the values from the <i>session</i> argument within the module as we see fit. To simplify things further, it can be assigned to the same variables and the rest of the code will remain the same: <figure class="highlight"> <pre class="language-r"><code class="language-r" data-lang="r"> # assign just not to use whole object all the time  plot1vars &lt;- session$userData$plot1vars  plot2vars &lt;- session$userData$plot2vars  dataset &lt;- session$userData$dataset </code></pre> </figure> One of the key advantages of this solution is that we can call our module in different places, for instance nested in another module on a separate modal screen, without having to consider whether the variables will be available for it there or if we need to recall them. They are always there waiting for us in the <i>session</i> argument! Please note: while one of the advantages of Shiny modules is that they can be easily transferred to other applications, reusing objects passed through <i>session</i> violates module independence – there is code inside the module that uses external objects without stating them explicitly as server arguments. Although session technical is one such parameter, it is not clear what objects it is required to contain – session is a huge list object, and we are interested in the contents of its sublist <i>userData</i>. Therefore, it is important to keep your code organized.. First, each module should contain detailed documentation. Second, the names of session’s objects should be reassigned to simpler names at the beginning of the module to avoid missing any dependencies. To sum up: keeping the results of the modules in a <i>session </i>object will help you organize the app content and simplify the objects flow logic. It is faster than managing all of the dependencies between modules. We recommend this approach for advanced Shiny apps. &nbsp; <h2>Follow Us for More</h2><ul><li>Follow <a href="https://twitter.com/appsilon">@Appsilon</a> on Twitter</li><li>Follow Appsilon on <a href="https://www.linkedin.com/company/appsilon">LinkedIn</a></li><li>Try out our R Shiny <a href="https://appsilon.com/opensource/">open source</a> packages</li></ul> <div class="mailmunch-forms-after-post"> <div class="mailmunch-forms-widget-687519"></div> </div>

Have questions or insights?

Engage with experts, share ideas and take your data journey to the next level!
Explore Possibilities

Share Your Data Goals with Us

From advanced analytics to platform development and pharma consulting, we craft solutions tailored to your needs.

Talk to our Experts
r
tutorials
shiny dashboards