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

100 Views Asked by At

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)

2

There are 2 best solutions below

0
On BEST ANSWER

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

1
On

For anyone curious, I think I found a workaround:

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) {
  row_values <- reactiveValues(population = -1, city="Timbuktu")



  output$selected_table = renderReactable({

    # get data for checked row
    selected_pop = row_values$population
    selected_city = row_values$city
    if (!is.null(city_data)) {
      reactable(city_data,

                columns = list(
                  
                  city = colDef(
                    style = function(value) {
                      if (selected_city == "Timbuktu") {
                        list(background = "white")
                      } else if (value == selected_city) {
                        list(background = "#AAAAAA")
                      } else {
                        list(background = "white")
                      }
                    }
                  ),
                  
                  
                  population = colDef(
                    style = function(value) {
                      
                      if (selected_pop == -1) {
                        list(background = "white")
                      } else if (value > selected_pop) {
                        list(background = "green")
                      } else {
                        list(background = "red")
                      }
                    }
                  )
                ),
                selection = "single"
      )
    }

  })

  observeEvent(as.character(getReactableState("selected_table","selected")), {

    checked_row_index = getReactableState("selected_table", "selected")
    checked_row = city_data[checked_row_index, ]
    
    if (!is.null(checked_row_index)) {
      row_values$population = checked_row$population
    }
    
    if (!is.null(checked_row_index)) {
      row_values$city = checked_row$city
    }

  })

}


shinyApp(ui = ui, server = server)