Skip to content

Conversation

gadenbuie
Copy link
Collaborator

@gadenbuie gadenbuie commented May 27, 2024

Starting a PR to help the discussion we've had around reactive.value objects and mutability.

Before - Reactivity with mutable objects

The key problem can be seen in the example from Reactivity | Mutable objects. In this example, currently, even if you call .set() on a reactive object, if the object was mutated in place .set() doesn't invalidate reactivity because the new value and the current value are already the same.

from shiny import reactive
from shiny.express import input, render, ui

ui.input_numeric("x", "Enter a value to add to the list:", 1)
ui.input_action_button("submit", "Add Value")


@render.code
def out():
    return f"Values: {user_provided_values()}"


# Stores all the values the user has submitted so far
user_provided_values = reactive.value([])


@reactive.effect
@reactive.event(input.submit)
def add_value_to_list():
    # WATCHOUT! This doesn't work as expected!
    values = user_provided_values()
    values.append(input.x())
    user_provided_values.set(values)

Kapture 2024-05-27 at 14 55 03

The best approach for updating the reactive value is to create a new instance of the reactive value, e.g.

@reactive.effect
@reactive.event(input.submit)
def add_value_to_list():
    values = user_provided_values()
    user_provided_values.set([*values, input.x()])

This works well for lists -- once you become aware of the problem, of course -- and dictionaries, but is easy to run into with other mutable objects that might be harder to copy.

After -- Compare new (mutable object) values by hash

This PR uses xxHash to hash mutable objects to then compare the new value with the hash of the old value when .set() was last called. After this change the example above works as most people would expect.

Kapture 2024-05-27 at 14 55 50

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant