glin / reactable

Interactive data tables for R
https://glin.github.io/reactable
Other
613 stars 79 forks source link

nesting 2 tables problem #272

Open andresimi opened 1 year ago

andresimi commented 1 year ago

Hi, I am having trouble when nesting two tables in reactable. As you can see, the third table don't match with the ID of the row clicked. Here is a reprodutive example

library(tidyverse)
library(reactable)

df1 <- tibble(id = 1:10,
              info = letters[1:10],
              info2 = 11:20)
df1
#> # A tibble: 10 × 3
#>       id info  info2
#>    <int> <chr> <int>
#>  1     1 a        11
#>  2     2 b        12
#>  3     3 c        13
#>  4     4 d        14
#>  5     5 e        15
#>  6     6 f        16
#>  7     7 g        17
#>  8     8 h        18
#>  9     9 i        19
#> 10    10 j        20

df2 <- tibble(id = rep(1:10, 2),
              info = c(rep("a", 10), rep("b", 10)),
              info2 = c(rep("c", 10), rep("d", 10)))
df2
#> # A tibble: 20 × 3
#>       id info  info2
#>    <int> <chr> <chr>
#>  1     1 a     c    
#>  2     2 a     c    
#>  3     3 a     c    
#>  4     4 a     c    
#>  5     5 a     c    
#>  6     6 a     c    
#>  7     7 a     c    
#>  8     8 a     c    
#>  9     9 a     c    
#> 10    10 a     c    
#> 11     1 b     d    
#> 12     2 b     d    
#> 13     3 b     d    
#> 14     4 b     d    
#> 15     5 b     d    
#> 16     6 b     d    
#> 17     7 b     d    
#> 18     8 b     d    
#> 19     9 b     d    
#> 20    10 b     d

df3 <- tibble(id = rep(1:10, 3),
              info = c(rep("y", 10), rep("x", 10), rep("g", 10)),
              info2 = c(rep("w", 10), rep("s", 10), rep("u", 10)))
df3
#> # A tibble: 30 × 3
#>       id info  info2
#>    <int> <chr> <chr>
#>  1     1 y     w    
#>  2     2 y     w    
#>  3     3 y     w    
#>  4     4 y     w    
#>  5     5 y     w    
#>  6     6 y     w    
#>  7     7 y     w    
#>  8     8 y     w    
#>  9     9 y     w    
#> 10    10 y     w    
#> # … with 20 more rows

reactable(df1,
          details = function(index){
            data <- df2 %>% filter(id==df1$id[index])
            reactable(data,
                      details = function(index){
                        data2 <- df3 %>% filter(id==df2$id[index])
                        data2 %>% reactable()
                      })
          })

image

Thank you for your support!

glin commented 1 year ago

Sorry, I'm having trouble understanding the problem. Could you explain in more detail what the expected output should be, or what the code is trying to achieve? The code appears to be working "correctly" when I trace through it, but I also don't know if that was the intent.

The screenshot shows the first row (index = 1) being expanded on the first-level nested table. Replacing index with 1, the expected data would be exactly as the screenshot shows:

> data2 <- df3 %>% filter(id == df2$id[1])
> data2
# A tibble: 3 × 3
     id info  info2
  <int> <chr> <chr>
1     1 y     w    
2     1 x     s    
3     1 g     u    

However, taking a guess, you may have wanted to use the index argument from the first details function, and the index argument from the second details function is masking that. If that's the case, you can try removing or renaming the unused index argument from the second details function:

reactable(df1,
          details = function(index){
            data <- df2 %>% filter(id==df1$id[index])
            reactable(data,
                      details = function(){ # Removed the index argument here. Alternatively, rename it to "index2" or something
                        data2 <- df3 %>% filter(id==df2$id[index])
                        data2 %>% reactable()
                      })
          })

Although the examples show every row details function with an index argument, that argument is optional and doesn't even have to be named index.

andresimi commented 1 year ago

Hi, thank you for prompt response. I think the expected behaviour would be that the id signalled by the red arrow in the picture would be "4". Your code, removing the index argument worked (and I couldn't understand why). However, when I split the code building functions for level it didn't. I tried to rename the second index to index2 and it didn't work as well...

I was wondering if something like this would be possible? As the tables I built are quite heavy coded.

tab2 <- function(data){
  reactable(data,
            details = function(index2){
              data2 <- df3 %>% filter(id==df2$id[index2])
              data2 %>% reactable()
            })
}

reactable(df1,
          details = function(index){
            data <- df2 %>% filter(id==df1$id[index])
            data %>% tab2
          })
glin commented 1 year ago

Okay, so it sounds like in your nested row details function, you do want to use the row index of the original parent table rather than the row index of the nested table. The nested functions make things a little confusing, but you essentially have a naming clash with two different index arguments overlapping each other. Here's a simplified example that might help.

In the problematic example, there's a nested function that uses the same index argument as its parent function. Within the nested function, you really want the index from the parent function, but the nested function's index argument takes precedence over that:

parentDetails <- function(index) {
  nestedDetails <- function(index) {
    # `index` here is for the nested table, NOT the parent table. We really want `index`
    # from parentDetails(), but the `index` argument of the nested function overrides
    # the `index` argument of the parent function.
    index
  }
}

To fix the naming clash, you can use a different argument name for the nested function, like index2. Then you can access both the parent function's index argument or the nested function's index2 argument:

parentDetails <- function(index) {
  nestedDetails <- function(index2) {
    # `index` here is for the parent table. The nested function's `index2` argument
    # doesn't overlap the parent function.
    index
  }
}

With the separate tab2 function to create a nested table, yes it would be possible to use, but a little trickier as you need access to the parent table's current row index. To do this, you can add a second argument for the parent table's row index, like tab2(data, index) (where index is the parent table's row index). Just make sure that within nested functions, argument names never overlap with other variable names that need to use.

tab2 <- function(data, index){
  reactable(data,
            details = function(index2){
              data2 <- df3 %>% filter(id==df2$id[index])
              data2 %>% reactable()
            })
}

reactable(df1,
          details = function(index){
            data <- df2 %>% filter(id==df1$id[index])
            tab2(data, index)
          })