I want to update a dataframe with user input in an R Shiny app. The user selects a row of data, and then chooses the value to update the Species column with using selectInput
. The updates need to accumulate each time, i.e., the new updates update the previously updated data. I tried using reactiveValues
as per this answer, but couldn't get it to work.
library(shiny)
library(reactable)
library(tidyverse)
iris_df = iris %>%
mutate(
id = row_number(),
Species = as.character(Species)
)
ui <- fluidPage(
navbarPage(
"HELP!",
tabPanel(
"Iris",
sidebarLayout(
sidebarPanel(
selectInput("update_species", "Update species", choices = c("Rose", "Daffodil"))
),
mainPanel(fluidRow(reactableOutput("iris")))
)
)
)
)
server <- function(input, output) {
observeEvent(input$update_species, {
iris_df = iris_df %>%
mutate(Species = case_when(id == selected_row() ~ input$update_species, TRUE ~ Species))
})
selected_row = reactive(getReactableState("iris", "selected"))
output$iris = renderReactable({
reactable(
iris_df,
selection = "single",
)
})
}
shinyApp(ui = ui, server = server)
The first issue with your code is that the observeEvent
is triggered by input$update_species
which results in an error if no row was selected in which case selected_row()
is NULL
. To prevent that you could add a req(selected_row())
or use selected_row()
to trigger the observeEvent
as I do in my code below.
Next, as you already realized you need a reactiveVal
or a reactiveValues
to actually update the dataframe inside the observeEvent
based on the user choice.
library(shiny)
library(reactable)
library(tidyverse)
iris_df <- iris %>%
mutate(
id = row_number(),
Species = as.character(Species)
)
ui <- fluidPage(
navbarPage(
"HELP!",
tabPanel(
"Iris",
sidebarLayout(
sidebarPanel(
selectInput("update_species", "Update species", choices = unique(iris_df$Species))
),
mainPanel(fluidRow(reactableOutput("iris")))
)
)
)
)
server <- function(input, output) {
iris_df <- reactiveVal(iris_df)
observeEvent(selected_row(), {
iris_df(iris_df() %>%
mutate(Species = case_when(id == selected_row() ~ input$update_species, TRUE ~ Species)))
})
selected_row <- reactive(getReactableState("iris", "selected"))
output$iris <- renderReactable({
reactable(
iris_df(),
selection = "single",
)
})
}
shinyApp(ui = ui, server = server)
And this is an example output after updating rows 1, 4 and 7: