I want to make a reactable table with single-select rows in Shiny such that whenever I click on a row, the colors of the other rows on the table are formatted based on whether or not their values are greater than or less than the row I selected. For example, when I select the Houston, the other cells in the population column should all be red since they are all less than Houston's population.
This formatting works, but the only problem is that it flashes on the screen for about a tenth of a second and then disappears and my row gets unchecked. How can I make the formatting persist?
Here is a minimal reproducible example:
library(shiny)
ui <- fluidPage(
reactableOutput("selected_table")
)
city_data <- data.frame(
city = c("New Orleans", "Houston", "San Antonio", "Dallas", "Austin", "Tampa"),
population = c(376971, 22880000, 1452000, 12880000, 964177, 387050)
)
server <- function(input, output, session) {
output$selected_table = renderReactable({
# get data for checked row
checked_row_index = getReactableState("selected_table", "selected")
checked_row = city_data[checked_row_index, ]
if (!is.null(city_data)) {
reactable(city_data,
columns = list(
population = colDef(
style = function(value) {
if (!is.null(checked_row_index)) {
if (!is.null(checked_row$population)) {
if (value < checked_row$population) {
# format red if value less than selected row
list(background = "#f8baba")
} else {
# format green if value greater than selected row
list(background = "#baf8ba")
}
} else {
# if value for cell null, format grey
list(background = "#eee")
}
} else {
# if no row selected, format cell with color grey
list(background = "#eee")
}
}
)
),
selection = "single"
)
}
})
}
shinyApp(ui = ui, server = server)
The issue is that each time you select a row, your table is rendered again and the selected value is set to NULL
which triggers a new "update" and sets the colors back to the default. Instead, to prevent that you have to set the defaultSelected
. Also I would suggest to move checked_row_index
and checked_row
outside of the renderReactable
and make them reactive
s on their own:
library(shiny)
library(reactable)
city_data <- data.frame(
city = c("New Orleans", "Houston", "San Antonio", "Dallas", "Austin", "Tampa"),
population = c(376971, 22880000, 1452000, 12880000, 964177, 387050)
)
ui <- fluidPage(
reactableOutput("selected_table")
)
server <- function(input, output, session) {
checked_row_index <- reactive({
getReactableState("selected_table", "selected")
})
checked_row <- reactive({
city_data[checked_row_index(), ]
})
output$selected_table <- renderReactable({
reactable(city_data,
columns = list(
population = colDef(
style = function(value) {
if (!is.null(checked_row_index())) {
if (!is.null(checked_row()$population)) {
if (value < checked_row()$population) {
# format red if value less than selected row
list(background = "#f8baba")
} else {
# format green if value greater than selected row
list(background = "#baf8ba")
}
} else {
# if value for cell null, format grey
list(background = "#eee")
}
} else {
# if no row selected, format cell with color grey
list(background = "#eee")
}
}
)
),
selection = "single",
defaultSelected = if (!is.null(checked_row_index())) checked_row_index()
)
})
}
shinyApp(ui = ui, server = server)