2

I am making a shiny app with a reactable where the user can choose how to sort the table. The trouble is when the table refreshes the sorting goes back to the default sorting specified when the table is created. I want to be able to have the table refresh to the sorting specifications the user has chosen.

I know this is possible by using a selectInput or pickerInput or some other input along those lines which can feed to the defaultSorted argument in the reactable but I am looking for something a little bit more organic without the need of an external input. Currently the getReactableState only provides page size/number and selection references.

Below is a toy example where if the user sorts by the first 2 columns and then clicks toggle, the table refreshes and goes back to its original sort state.

library(shiny)
library(reactable)


ui <- fluidPage(
  actionButton("toggle", "Toggle"),
  reactableOutput("table")
)

server <- function(input, output, session){
  
  df <- eventReactive(input$toggle, {
    mtcars[c(1:2,sample(3:ncol(mtcars), size = 3))]
  })
  
  output$table <- renderReactable({
    reactable(df(),
              filterable = FALSE, 
              sortable = TRUE)
  })
  
}


shinyApp(ui, server)

zimia
  • 930
  • 3
  • 16

1 Answers1

2

You could use data argument of updateReactable:

data should have the same columns as the original table data. When updating data, the selected rows, expanded rows, and current page will reset unless explicitly specified. All other state will persist, including sorting, filtering, and grouping state

If you don't use rownames with your real data, this should be OK : sorting order is kept.
If you use rownames, there seems to be an issue with rownames disappearing : this is why I put rownames in the model column below and remove rownames.

library(shiny)
library(reactable)


ui <- fluidPage(
  actionButton("toggle", "Toggle"),
  reactableOutput("table")
)

server <- function(input, output, session){
  
  ref <- mtcars[c(1:2,sample(3:ncol(mtcars), size = 3))]
  ref$model <- rownames(ref)
  rownames(ref) <- NULL
  init <- ref[0,]
  
  
  output$table <- renderReactable({
    reactable(init,
              filterable = FALSE, 
              sortable = TRUE)
  })
  
  df <- eventReactive(input$toggle, {
   ref[sample(1:nrow(ref),20),]
  })
  
  observeEvent(input$toggle, {
    updateReactable("table", data = df())
  })
  
  
  
}
Waldi
  • 39,242
  • 6
  • 30
  • 78
  • 1
    Hi thanks for your answer, however, the problem is the `reactable` will always be reading a `reactiveVal` as the data (generated from elsewhere in the app) and this `reactiveVal` can be changed by the user in another part of the app. With the mtcars example we can set a ref but this wont be possible in reality. The table will refresh any time the `reactiveVal` is updated. In that case, should I default the `reactiveVal` to start with an empty df with the same column headers? – zimia Jul 28 '22 at 10:41
  • Yes, exactly : an empty data.frame wil do for init, see my edit. – Waldi Jul 28 '22 at 11:31