Search code examples
rshinyshiny-reactivityreactable

Row color formatting on reactable table appears for a second and then disappears


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)


Solution

  • 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 reactives 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)
    

    enter image description here