shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Allow removal of value from reactiveValues list

Open daattali opened this issue 5 years ago • 7 comments

As far as I know, there is no way to remove a value from a reactiveValues list. Unlike a regular list, when setting a value to NULL, it still prints in the reactiveValues.

This has been previously discussed on the Google group https://groups.google.com/forum/#!topic/shiny-discuss/4XxbaHQoE3o

@wch brought up the argument of

deleting items is that the behavior for an item deleted from a reactiveValues object is not well-defined - what should happen if a reactive expression takes a dependency on values$x, but then values$x is removed?

I would say that's not necessarily a big issue - it's already possible to remove reactive values that have potential dependencies. We can remove UI that has inputs in it, and anything that depends on a deleted input simply never receives any future invalidations from that value. I think that would be fine.

Super hacky workaround?

Purely out of curiousity I wanted to see how I can do this without shiny's support. I haven't tested it much and I don't know if there are any repercussions for doing this (I'm sure there may be!), but the following two lines can work. Very hacky, I do not actually recommend this.

rm("name", envir = .subset2(rv, "impl")$.values)
 .subset2(rv, "impl")$.valuesDeps$invalidate()

EDIT:

The above no longer works in the newest version of shiny. You now need to use:

.subset2(rv, "impl")$.values$remove("name")

in order to remove an element from the reactiveValues list. But the second line from above, the one that causes invalidation, also doesn't work anymore and I'm not sure how to replace it.

daattali avatar May 15 '19 18:05 daattali

@daattali by wrapping the condition under which you want the value to appear in the reactiveValue list, it seems you toggle whether the value exists or not. When the condition passes into the else its as if the slot/value never existed

rvs <- reactiveValues(slot = list(NULL))

observe({
     if (condition) rvs$slot["a"] <- foo
     else rvs$slot["a"] <- NULL
   })

hragbalian avatar Jun 12 '19 00:06 hragbalian

@hragbalian what your code is doing is creating a list and removing a value from a list. It's nested inside reactiveValues. In a regular list, giving an element a NULL value does remove it from the list, which is what you're doing. But if you tried to assign NULL directly into the slot element, that would be different.

daattali avatar Jun 12 '19 03:06 daattali

@daattali I think you're right that the semantics of removal make sense. You can already take a dependency on a key that isn't represented (yet) in the reactive values object. If a context has a dependency on an existing key and that key is removed, the context should be invalidated.

jcheng5 avatar Jun 12 '19 04:06 jcheng5

~~@jcheng5 @alandipert it seems like shiny has undergone some internal refactoring and now my hacky solution doesn't work (yep, that's why we never use undocumented "API"!)~~

~~I found that the actual removal of the element can now be done with .subset2(rv, "impl")$.values$remove("name") instead of rm("name", envir = .subset2(rv, "impl")$.values)~~

~~But I couldn't figure out what to replace .subset2(rv, "impl")$.valuesDeps$invalidate() with in order to invalidate the object. Is there some line I could use to cause that to happen?~~

EDIT: I found a way to make it work, see the edit in my original post

daattali avatar Aug 29 '19 22:08 daattali

This saved me a lot of hassle. I think it goes against my (potentially mis-)conception that assigning NULL deletes a (named) element of a list AND that reactiveValues behave like (reactive) lists.

adimajo avatar Nov 19 '20 14:11 adimajo

Wouldn't this deserve to be an accessible function? e.g. removeReactiveValuesIndex <- function(rv, ind) { .subset2(rv, "impl")$.values$remove(ind) } (yup, I did it quick and dirty, there are probably better ways to). The thing is removing an item from a list-like object is elementary, therefore having a quick solution available would help a lot.

earnaud avatar Feb 28 '22 10:02 earnaud

This will not remove the index from names(rv) (see here and here)

The following worked for me

rv_r6 = .subset2(rv, "impl")
rv_r6$.values$remove(ind)
rv_r6$.nameOrder = setdiff(rv_r6$.nameOrder, ind)

j-kreis avatar Oct 26 '23 12:10 j-kreis