Skip to content

Developer Information

Ashley Baldry edited this page Oct 28, 2026 · 2 revisions

How shinyreprex Reads a Reactive

shinyreprex doesn't run your reactive to see what it does — it reads its expression and rewrites it into a standalone script. Four steps, each with its own place in the code.

1. Find the reactive's root

A shiny::reactive() doesn't carry its own environment where you'd expect — bindEvent/bindCache wrap it in extra layers. reactive_expression() (R/reactive_expression.R) unwraps those layers to get the reactive's unevaluated body (body) and the module environment it was defined in (env). That environment is the "root" everything else resolves names against.

2. Evaluate the leaves (inputs)

Walking the body, each call is classified by is_input_call(), is_reactive_values_call(), etc. (R/call_chunk_utils.R). A leaf — input$x, session$userData$y — is evaluated against env with construct_reactive() and baked into the script as a literal value (call_chunk_subset.R). This is why the output is a snapshot: it freezes whatever the leaf resolved to at the time reprex_reactive() was called, not a live binding.

3. Deparse the branches (nested reactives)

A call that resolves to another reactive (checked by is_reactive_call()) is itself a branch, not a leaf. chunk_reactive.R recurses into it via reactive_expression() again, and the S7 generic repro_chunk dispatches on the node's class — call_chunk_if.R, call_chunk_switch.R, call_chunk_function.R, etc. — to handle if/switch/function calls differently. Each dispatch accumulates into a Repro S7 object (Repro_S7.R), which tracks code, packages, and prerequisites (definitions the current chunk depends on).

4. Linearize into a script

Repro@calls (Repro_S7.R:57-77) is where the tree becomes a line-by-line script: library() calls first, then prerequisites (deparsed via constructive::deparse_call), then the chunk's own code — in that order, so a reactive's dependencies are always defined before it's used. Repro@script runs that through styler::style_text() for formatting. reprex_reactive() is the public entry point that kicks this whole walk off for a given reactive.

Where to extend this

Adding support for a new call shape (a new control-flow construct, a new way of referencing inputs) means adding a new S7::method for repro_chunk or repro_call_chunk, following the pattern in any of the call_chunk_*.R files.