2016-09-11 16 views
21

Câu hỏi này có liên quan đến this one. Cả hai có thể tạo ra cùng một chức năng, nhưng việc triển khai hơi khác một chút. Một khác biệt đáng kể là reactiveValue là một vùng chứa có thể có nhiều giá trị, như input$. Trong chức năng shiny documentation thường được triển khai bằng cách sử dụng reactive(), nhưng trong hầu hết các trường hợp, tôi thấy reactiveValues() thuận tiện hơn. Có bắt ở đây không? Có sự khác biệt lớn nào khác giữa hai điều mà tôi có thể không nhận thức được không? Hai đoạn mã này có tương đương nhau không?R Sáng bóng: reactiveValues ​​vs reactive

Xem cùng example code triển khai sử dụng:

  1. một biểu hiện phản ứng:

    library(shiny) 
    
    ui <- fluidPage( 
        shiny::numericInput(inputId = 'n',label = 'n',value = 2), 
        shiny::textOutput('nthValue'), 
        shiny::textOutput('nthValueInv') 
    ) 
    
    fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2)) 
    
    server<-shinyServer(function(input, output, session) { 
        currentFib   <- reactive({ fib(as.numeric(input$n)) }) 
        output$nthValue <- renderText({ currentFib() }) 
        output$nthValueInv <- renderText({ 1/currentFib() }) 
    }) 
    
    shinyApp(ui = ui, server = server) 
    
  2. một giá trị phản ứng:

    library(shiny) 
    
    ui <- fluidPage( 
        shiny::numericInput(inputId = 'n',label = 'n',value = 2), 
        shiny::textOutput('nthValue'), 
        shiny::textOutput('nthValueInv') 
    ) 
    
    fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2)) 
    
    server<-shinyServer(function(input, output, session) { 
        myReactives <- reactiveValues() 
        observe( myReactives$currentFib <- fib(as.numeric(input$n)) ) 
        output$nthValue <- renderText({ myReactives$currentFib }) 
        output$nthValueInv <- renderText({ 1/myReactives$currentFib }) 
    }) 
    
    shinyApp(ui = ui, server = server) 
    
+2

Khi bạn hoàn tất, hãy đặt chủ đề này thành tài liệu tham khảo http://stackoverflow.com/documentation/shiny/topics –

Trả lời

34

Có sự bắt, mặc dù nó sẽ không được phát trong ví dụ của bạn.

Các bóng nhà phát triển thiết kế reactive()lười biếng, có nghĩa là biểu thức chứa trong nó sẽ chỉ được thực hiện khi nó được gọi bằng một trong những người phụ thuộc của nó. Khi một trong những phụ thuộc phản ứng của nó bị thay đổi, nó xóa bộ nhớ đệm của nó và thông báo cho người phụ thuộc của chính nó, nhưng nó không được thực thi cho đến khi được yêu cầu bởi một trong những người phụ thuộc đó. (Vì vậy, nếu, chẳng hạn, nó duy nhất phụ thuộc là một yếu tố textOutput() trên một tab ẩn, nó sẽ không thực sự được thực hiện trừ khi và cho đến khi tab được mở ra.)

observe(), mặt khác, là háo hức; biểu thức mà nó chứa sẽ được thực thi ngay lập tức bất cứ khi nào một trong các phụ thuộc phản ứng của nó bị thay đổi - ngay cả khi giá trị của nó không cần thiết bởi bất kỳ người phụ thuộc nào của nó (và trên thực tế ngay cả khi không phụ thuộc). Sự háo hức như vậy là mong muốn khi bạn gọi observe() cho các tác dụng phụ của nó, nhưng có thể là lãng phí khi bạn chỉ sử dụng nó để chuyển giá trị trả về của nội dung của nó sang các biểu thức phản ứng hoặc điểm cuối khác .

Joe Cheng giải thích sự khác biệt này khá tốt trong buổi thuyết trình Hội nghị nhà phát triển sáng bóng 2016 của mình về "Lập trình phản ứng hiệu quả", available here. Xem đặc biệt là bit bắt đầu khoảng 30:20 trong giờ thứ hai của bài thuyết trình. Nếu bạn xem cho đến 40:42 (chớp mắt và bạn sẽ bỏ lỡ nó!), Ông mô tả ngắn gọn hành vi của sự kết hợp observe()/reactiveValue() mà bạn thích.

+0

nhờ có thông tin chi tiết. Tôi đoán tin nhắn mang về nhà là sử dụng quan sát/reactiveValue có thể ít hiệu quả hơn. Nhưng .. như được chỉ bởi daattali có những trường hợp là reactiveValues ​​dễ dàng hơn để khái niệm hóa/thực hiện. Nó nên được giải thích tốt hơn trong các tài liệu sáng bóng. –

19

Đúng là hai cấu trúc tương tự, và nhiều lần bạn có thể sử dụng một trong hai cách để giải quyết vấn đề của mình. Nhưng thường có ý nghĩa hơn khi sử dụng một hoặc khác.

Trong trường hợp fibonacci, tôi nghĩ rằng sử dụng một biểu thức reactive() có ý nghĩa hơn, bởi vì currentFib là một giá trị mà nên được sửa đổi trong thời gian dự đoán rất cụ thể (ví dụ. Khi input$n thay đổi, giá trị phản ứng nên được cập nhật cho phù hợp, hoặc phản ứng cho thay đổi đó).

Nhưng trong một số trường hợp khác, việc sử dụng reactiveValues có thể đơn giản và tốt hơn. Tôi sẽ trình bày hai ví dụ.

Đầu tiên, bất cứ khi nào bạn có biến số mà bạn nghĩ là có trạng thái nào đó (thay vì chỉ phản ứng với giá trị khác đang được cập nhật), tôi nghĩ sử dụng reactiveValues là tốt hơn.

Ví dụ:

library(shiny) 

ui <- fluidPage(
    "Total:", 
    textOutput("total", inline = TRUE), 
    actionButton("add1", "Add 1"), 
    actionButton("add5", "Add 5") 
) 

server <- function(input, output, session) { 
    values <- reactiveValues(total = 0) 

    observeEvent(input$add1, { 
    values$total <- values$total + 1 
    }) 
    observeEvent(input$add5, { 
    values$total <- values$total + 5 
    }) 
    output$total <- renderText({ 
    values$total 
    }) 
} 

shinyApp(ui = ui, server = server) 

Trong đoạn mã trên, chúng ta có một biến total có nhà nước có thể thay đổi, và đó là trực quan hơn nhiều để nghĩ về nó như là một biến đặc trưng và sử dụng nó như vậy. Đây là trường hợp phổ biến nhất khi tôi sử dụng reactiveValues.

Tôi cũng sử dụng reactiveValues khi biến có thể được cập nhật ở nhiều nơi. Để mượn từ ví dụ fibonacci, hãy xem xét các ứng dụng sáng bóng sau, nơi mà các số n có thể được thiết lập bởi một trong hai đầu vào:

library(shiny) 

fib <- function(n) ifelse(n < 3, 1, fib(n - 1) + fib(n - 2)) 

ui <- fluidPage(
    selectInput("nselect", "Choose a pre-defined number", 1:10), 
    numericInput("nfree", "Or type any number", 1), 
    "Fib number:", 
    textOutput("nthval", inline = TRUE) 
) 

server <- function(input, output, session) { 
    values <- reactiveValues(n = 1) 

    observeEvent(input$nselect, { 
    values$n <- input$nselect 
    }) 
    observeEvent(input$nfree, { 
    values$n <- input$nfree 
    }) 
    output$nthval <- renderText({ 
    fib(as.integer(values$n)) 
    }) 
} 

shinyApp(ui = ui, server = server) 

Ví dụ này có vẻ hơi lạ trong bối cảnh fibonacci, nhưng hy vọng bạn có thể thấy làm thế nào trong một số ứng dụng phức tạp khác, đôi khi bạn có thể muốn đặt giá trị của một biến ở những vị trí khác nhau và nó có thể trực quan hơn để làm điều đó bằng cách sử dụng một reactiveValue thay vì một biểu thức phản ứng phải được thực hiện trong một khối.

Hy vọng điều này hữu ích và có ý nghĩa. Tất nhiên, đây chỉ là vấn đề cá nhân của tôi đối với chủ đề, nó không nhất thiết là những gì các nhà phát triển sáng bóng dự định, nhưng đây là cách tôi đã học cách sử dụng hai phương pháp.

Các vấn đề liên quan