Implement custom render functions — createRenderFunction

v1.7.0|Source: R/shinywrappers.R, R/utils-lang.R

Description

Developer-facing utilities for implementing a custom renderXXX() function. Before using these utilities directly, consider using the htmlwidgets package to implement custom outputs (i.e., custom renderXXX()/xxxOutput() functions). That said, these utilities can be used more directly if a full-blown htmlwidget isn't needed and/or the user-supplied reactive expression needs to be wrapped in additional call(s).

createRenderFunction(
  func,
  transform = function(value, session, name, ...) value,
  outputFunc = NULL,
  outputArgs = NULL,
  cacheHint = "auto",
  cacheWriteHook = NULL,
  cacheReadHook = NULL
)

quoToFunction(q, label = sys.call(-1)[[1]], ..stacktraceon = FALSE)

installExprFunction(
  expr,
  name,
  eval.env = parent.frame(2),
  quoted = FALSE,
  assign.env = parent.frame(1),
  label = sys.call(-1)[[1]],
  wrappedWithLabel = TRUE,
  ..stacktraceon = FALSE
)

Arguments

func

A function without parameters, that returns user data. If the returned value is a promise, then the render function will proceed in async mode.

transform

A function that takes four arguments: value, session, name, and ... (for future-proofing). This function will be invoked each time a value is returned from func, and is responsible for changing the value into a JSON-ready value to be JSON-encoded and sent to the browser.

outputFunc

The UI function that is used (or most commonly used) with this render function. This can be used in R Markdown documents to create complete output widgets out of just the render function.

outputArgs

A list of arguments to pass to the uiFunc. Render functions should include outputArgs = list() in their own parameter list, and pass through the value to markRenderFunction, to allow app authors to customize outputs. (Currently, this is only supported for dynamically generated UIs, such as those created by Shiny code snippets embedded in R Markdown documents).

cacheHint

One of "auto", FALSE, or some other information to identify this instance for caching using bindCache(). If "auto", it will try to automatically infer caching information. If FALSE, do not allow caching for the object. Some render functions (such as renderPlot) contain internal state that makes them unsuitable for caching.

cacheWriteHook

Used if the render function is passed to bindCache(). This is an optional callback function to invoke before saving the value from the render function to the cache. This function must accept one argument, the value returned from renderFunc, and should return the value to store in the cache.

cacheReadHook

Used if the render function is passed to bindCache(). This is an optional callback function to invoke after reading a value from the cache (if there is a cache hit). The function will be passed one argument, the value retrieved from the cache. This can be useful when some side effect needs to occur for a render function to behave correctly. For example, some render functions call createWebDependency() so that Shiny is able to serve JS and CSS resources.

q

Quosure of the expression x. When capturing expressions to create your quosure, it is recommended to use enquo0() to not unquote the object too early. See enquo0() for more details.

label

A label for the object to be shown in the debugger. Defaults to the name of the calling function.

expr

A quoted or unquoted expression, or a quosure.

name

The name the function should be given

eval.env

The desired environment for the function. Defaults to the calling environment two steps back.

quoted

Is the expression quoted?

assign.env

The environment in which the function should be assigned.

wrappedWithLabel, ..stacktraceon

Advanced use only. For stack manipulation purposes; see stacktrace().

Value

An annotated render function, ready to be assigned to an output slot.

Details

To implement a custom renderXXX() function, essentially 2 things are needed:

  1. Capture the user's reactive expression as a function.

    • New renderXXX() functions can use quoToFunction() for this, but already existing renderXXX() functions that contain env and quoted parameters may want to continue using installExprFunction() for better legacy support (see examples).

  2. Flag the resulting function (from 1) as a Shiny rendering function and also provide a UI container for displaying the result of the rendering function.

    • createRenderFunction() is currently recommended (instead of markRenderFunction()) for this step (see examples).

Functions

  • quoToFunction: convert a quosure to a function.

  • installExprFunction: converts a user's reactive expr into a function that's assigned to a name in the assign.env.

Examples

# A custom render function that repeats the supplied value 3 times
renderTriple <- function(expr) {
  # Wrap user-supplied reactive expression into a function
  func <- quoToFunction(rlang::enquo0(expr))

  createRenderFunction(
    func,
    transform = function(value, session, name, ...) {
      paste(rep(value, 3), collapse=", ")
    },
    outputFunc = textOutput
  )
}

# For better legacy support, consider using installExprFunction() over quoToFunction()
renderTripleLegacy <- function(expr, env = parent.frame(), quoted = FALSE) {
  func <- installExprFunction(expr, "func", env, quoted)

  createRenderFunction(
    func,
    transform = function(value, session, name, ...) {
      paste(rep(value, 3), collapse=", ")
    },
    outputFunc = textOutput
  )
}

# Test render function from the console
reactiveConsole(TRUE)

v <- reactiveVal("basic")
r <- renderTriple({ v() })
r()
#> [1] "basic, basic, basic"

# User can supply quoted code via rlang::quo(). Note that evaluation of the
# expression happens when r2() is invoked, not when r2 is created.
q <- rlang::quo({ v() })
r2 <- rlang::inject(renderTriple(!!q))
v("rlang")
r2()
#> [1] "rlang, rlang, rlang"

# Supplying quoted code without rlang::quo() requires installExprFunction()
expr <- quote({ v() })
r3 <- renderTripleLegacy(expr, quoted = TRUE)
v("legacy")
r3()
#> [1] "legacy, legacy, legacy"

# The legacy approach also supports with quosures (env is ignored in this case)
q <- rlang::quo({ v() })
r4 <- renderTripleLegacy(q, quoted = TRUE)
v("legacy-rlang")
r4()
#> [1] "legacy-rlang, legacy-rlang, legacy-rlang"

# Turn off reactivity in the console
reactiveConsole(FALSE)