Reactive object bindings in a non-shiny context

前端 未结 5 898
轮回少年
轮回少年 2021-02-02 12:25

Actual question

How could you either approximate the reactive environment/behavior established by shiny functions or possibly even use these very functions in a

5条回答
  •  后悔当初
    2021-02-02 13:10

    (Tried to leave this as a comment but S.O. said it was too long.)

    Kudos for looking more closely at reactivity. You may find these two links helpful:

    • https://jcheng.shinyapps.io/reactivity-dsc2014
    • How does Meteor's reactivity work behind the scenes? (Shiny's reactive core was largely inspired by Meteor)

    So actually Shiny's reactivity can be used outside of Shiny applications--with two tricks.

    1. If you attempt to read a reactive expression or reactive value from the console, you'll get an error. I intentionally did this because in a fundamentally reactive system like Shiny it's almost always a bug to read a reactive value or expression from a non-reactive context (hopefully that sentence makes sense if you've read the two links above). However when you're driving at the console it's pretty reasonable to want to circumvent this check. So you can set options(shiny.suppressMissingContextError=TRUE) to make it go away.
    2. When you do stuff that triggers reactivity, observers aren't actually executed until you call shiny:::flushReact(). This is so that you can perform multiple updates and then let all the reactive code respond once, instead of recalculating with every update. For console use, you can ask Shiny to automatically call flushReact on every console prompt by using shiny:::setAutoflush(TRUE). Again, this is only needed for observers to work.

    An example that works today (execute this line by line at the console):

    library(shiny)
    options(shiny.suppressMissingContextError=TRUE)
    
    makeReactiveBinding("x_1")
    x_1 <- Sys.time()
    x_2 <- reactive(x_1 + 60*60*24)
    x_1
    x_2()
    x_1 <- Sys.time()
    x_1
    x_2()
    
    # Now let's try an observer
    shiny:::setAutoflush(TRUE)
    observe(print(paste("The time changed:", x_1)))
    x_1 <- Sys.time()
    

    I would recommend taking another look at leveraging Shiny's reactive abstractions more directly. I think you can achieve a syntax like this quite straightforwardly with makeActiveBinding (assuming you think this is better than what Shiny gives you today):

    where <- new.reactr()
    where$x_1 <- Sys.time()
    where$x_2 <- reactive(x_1 + 60*60*24)
    where$x_1  # Read x_1
    where$x_2  # Read x_2
    

    One key advantage to declaring reactive expressions using reactive() rather than setThis is that the former can easily and naturally model expressions that depend on multiple reactive values/expressions at once. Note that reactive expressions are both cached and lazy: if you modify x_1 it will not actually recalculate x_2 until you try to read x_2, and if you read x_2 again without x_1 having changed then it'll just return the previous value without recalculating.

    For a more functional twist on Shiny reactivity, see Hadley Wickham's new package https://github.com/hadley/shinySignals that is inspired by Elm.

    Hope that helps.

提交回复
热议问题