R shiny: How to get an reactive data frame updated each time pressing an actionButton without creating a new reactive data frame?
Asked Answered
V

1

41

My shiny app allows user to upload a csv by using fileInput and stored as an reactive object df_data. I then created a numericInput for user to enter a row number to delete from the data frame. However, I got an error about evaluation nested too deeply: infinite recursion / options(expressions=)?.

Below is my code for ui.R.

shinyUI(fluidPage(
  titlePanel("amend data frame"),

  mainPanel(
    fileInput("file", "Upload file"),

    numericInput("Delete", "Delete row:", 1, step = 1),
    actionButton("Go", "Delete!"),

    tableOutput("df_data")
  )
))

And below is my code for server.R.

shinyServer(function(input, output) {
  df_data <- reactive({
    read.csv(input$file$datapath)
  })

  df_data <- eventReactive(
    input$Go,
    df_data()[-input$Delete,]
  )

  output$df_data <- renderTable(df_data())
})

I have solved the problem by assigning the subsetted data frame to a new reactive data frame called, say, df_data2. But I want to make the user able to keep deleting different rows by inputting values in Delete row and press the Go button. Then this solution will not work as I will have to assign df_data3, df_data4 ... and I couldn't predict in advance how many times the user will press the Go button.

I am a new user to shiny for about 2 weeks and I have been searching for solutions about this for a week. Is there anyone who can help me? A million thanks !!

Vaporetto answered 18/4, 2015 at 11:45 Comment(0)
B
80

Below is a working solution. I created a reactiveValues to store the dataframe. When a file is chosen, the dataframe gets populated. When the delete button is pressed, that same dataframe gets a row deleted. The table always outputs whatever that dataframe object is holding. I hope this code can be a good learning material

runApp(shinyApp(
ui=(fluidPage(
  titlePanel("amend data frame"),

  mainPanel(
    fileInput("file", "Upload file"),

    numericInput("Delete", "Delete row:", 1, step = 1),
    actionButton("Go", "Delete!"),

    tableOutput("df_data_out")
  )
)),
server = (function(input, output) {
  values <- reactiveValues(df_data = NULL)

  observeEvent(input$file, {
    values$df_data <- read.csv(input$file$datapath)
  })

  observeEvent(input$Go, {
    temp <- values$df_data[-input$Delete, ]
    values$df_data <- temp

  })

  output$df_data_out <- renderTable(values$df_data)
})))
Broddy answered 28/5, 2015 at 9:17 Comment(6)
Thank you! That works very well! I would like to further ask that why it is necessary to use observeEvent() but not observe(). What is the difference between the two? Thank you!Vaporetto
You could use regular observe. The main difference is that observeEvent takes as an argument what reactive to listen to, so any other reactive values inside the expression are by default isolated. Also, it doesn't get fired when the reactive is NULL, which is often what you want when dealing with inputs (otherwise you usually end up with the first line inside an observe being if (is.null(input$xxx)) return())Broddy
I'm glad you think so :)Broddy
so there isn't a solution that just uses the reactive expression. You need to use reactiveValuesMessick
Is the solution described still the advised method of doing this? The question and answer are currently a few years old so maybe a confirmation of whether this is still the advised solution would be nice.Sporangium
@Jochem, I don't know if that's the best way, but this solution still holds. (august, 2020)Caramelize

© 2022 - 2024 — McMap. All rights reserved.