Skip to content

Default Scoping

By default, hooks are scoped to the smallest enclosing scope. This means that if you call a hook inside a function, the hook will only be scoped to that function. If you call a hook inside a class, the hook will only be scoped to that class. If you call a hook inside a module, the hook will only be scoped to that module.

Hooks scoping mechanism attempts to work in a very predictable way in order to avoid unexpected behavior.

Here is an example of how hooks are scoped to the smallest enclosing scope:

from hooks import use_state


def get_tasks():
  state, set_state = use_state({"tasks": []})
  return state["tasks"]


print(get_tasks())  # Output: []


def add_task(task: str):
  state, set_state = use_state()
  set_state({"tasks": state["tasks"] + [task]})


add_task("Do the dishes")
add_task("Do the laundry")
print(get_tasks())  # Output: []

As you can see in the example above, the get_tasks function is scoped to the get_tasks function, and the add_task function is scoped to the add_task function. This means that the get_tasks function will always return an empty list, and the add_task function will always add a task to an empty list.


Class scopes

When using hooks inside a class, the scope by default will be limited to that class according to the type of method that the hook is called in. For example, if you call a hook inside a classmethod, the hook will be scoped to the class and not the instance. If you call a hook inside an instancemethod, the hook will be scoped to the instance and not the class. If you call a hook inside a staticmethod, the hook will be scoped globally.

from hooks import use_state


class CounterClass:
    def instance_counter(self):
        count, set_count = use_state(0)
        set_count(count + 1)
        return count

    @classmethod
    def class_counter(cls):
        count, set_count = use_state(0)
        set_count(count + 1)
        return count

    @staticmethod
    def static_counter():
        count, set_count = use_state(0)
        set_count(count + 1)
        return count


counter = CounterClass()
print(counter.instance_counter())         # Output: 0
print(counter.instance_counter())         # Output: 1
print(CounterClass().instance_counter())  # Output: 0
print(CounterClass().instance_counter())  # Output: 0
print(CounterClass.class_counter())       # Output: 0
print(CounterClass.class_counter())       # Output: 1
print(CounterClass.static_counter())      # Output: 0

As you can see the default scoping mechanism for hooks inside a class is to scope the hook to the smallest enclosing scope and is predictable. However, you may want to change the default scoping mechanism for hooks inside a class. You can do this by using the hooks_scope decorator.


Scopes and function parameters

When using hooks inside a function, the scope by default will ignore function parameters. This means that if you call a hook inside a function, the hook will be scoped to the function and not the function parameters (Unless when using parameters as dependencies on use_effect).

from hooks import use_state


def get_count(other_param: str):
    count, set_count = use_state(0)
    set_count(count + 1)
    print(other_param)
    return count


print(get_count("Hello"))  # Output: 0
print(get_count("World"))  # Output: 1
print(get_count("This wont affect the state"))  # Output: 2

Next steps

Learn about scoping hooks with hooks_scope decorator.