Skip to content

Instantly share code, notes, and snippets.

@bpartridge
Last active June 10, 2022 09:29
Show Gist options
  • Save bpartridge/4b621d2c19bbd62b9f496211954b0ec2 to your computer and use it in GitHub Desktop.
Save bpartridge/4b621d2c19bbd62b9f496211954b0ec2 to your computer and use it in GitHub Desktop.
React Hooks as Python decorators

So Hooks finally started to "click" for me when I realized they're very similar to Python decorators. In fact, they're essentially Python-like decorators, curried against the renderer, in an alternate syntax that reduces duplicate typing.

To be clear: I'm NOT advocating for Hooks moving towards decorator syntax, and I agree with the reasons why this suggestion isn't the right direction to move. But I think that the comparison with decorators is useful nonetheless, and perhaps could guide some of the ways in which we introduce Hooks to skeptical stakeholders.

Consider, for a moment, that Python almost adopted this syntax: https://www.python.org/dev/peps/pep-0318/#community-consensus ...

using:
    classmethod
    synchronized(lock)
def func(cls):
    pass

That's incredibly similar to the use prefix that's becoming the convention for Hooks. In time, I think we'll come to see the presence of a "preamble" in a Javascript function - one that contains only function calls starting with the word use - as perfectly analogous to a decorator block, but one that can "inject" parameters without you needing to write the names twice. And that's a good thing; it means that Javascript as a language has become flexible enough that this type of emergent behavior can be implemented without changes to the language itself.

Additionally, I think that thinking in terms of Python can address some common criticisms of Hooks:

  • Hooks break the contract that anything in a Javascript function should support arbitrary control flow: Technically, even Python decorators are inside an execution context where you can do a lot of fancy control flow if you don't follow the @ shorthand syntax... but nobody would think about wanting to apply a decorator in a loop, or conditionally apply a decorator. The decorators themselves handle control flow, and that's exactly what the implementations of hooks do.

  • Hooks silently access global state: If you were a Python developer who always needed to write @synchronize(someReallyCommonLock) def func: ... everywhere, you might consider writing a curried decorator def _sync(f): return synchronize(someReallyCommonLock)(f) then just calling @_sync def func: ... everywhere instead. This is very reasonable. It's also exactly what React exposes to you. The fact that someReallyCommonLock, or in this case theInternalReactRendererState, is hidden from the user is actually a benefit.

  • Hooks carry a runtime cost: So does Example 4 here; it's an extra function call, on every call to the decorated function, that essentially just copies some references into new variables. Which is no more or less than what useState does every time it's called. We need to trust our interpreters and JITs that this type of jump is optimized.

So I, for one, welcome our new Hook overlords. It's taken me some time to come around to that opinion, but now I honestly can't imagine any changes to the core syntax that would improve this proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment