nosco / hx

A simple, easy to use library for React development in ClojureScript.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Enforcing "Rules of Hooks"

lilactown opened this issue · comments

Per the React docs:

Only Call Hooks at the Top Level

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, we’ll explain this in depth below.)

Only Call Hooks from React Functions

Don’t call Hooks from regular JavaScript functions. Instead, you can:

✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.

The React team has released an eslint plugin that helps enforce these rules.

I think it would be interesting to see how we could help CLJS developers follow these same conventions.

Here are some considerations:

  • It's better to make it easy to do the right thing, then hard to do the wrong thing
  • We want to have similar (highly prefer the same) ergonomics for code that uses hx and foreign code (e.g. npm libraries, CLJS libs that don't use hx)
  • We want to make sure that we don't break code that doesn't use hx 😅

My initial thought was a hooks macro that was similar to let, but to enforce that hooks can't be used outside of that macro, detect if hooks are being used in loops, etc.

We could do some silly things, like:

  • Patch React.useState, React.useEffect etc. to only work if inside a hooks block.
    This would break any external libs trying to use Hooks 😛
  • Enforce it only for Hooks using the hx.hooks namespace
    This would have inconsistent behavior with 3rd party code

Perhaps having it be purely syntax level would be best: e.g. it looks for either <- or use at the beginning of the symbol name, then enforces the top-level rules of hooks.

I'm not sure how we would enforce that Hooks must only be used in React component functions or custom Hooks in this case.

Posting this issue here to collect thoughts!

As a potential user of this lib, I like the purely syntax-based option the most. hx.react/fnc* could walk the body and warn if symbols beginning with use or <- are used in disallowed contexts. Might be hard to enumerate the allowed vs disallowed contexts though.

Off topic and feel free to disregard entirely, but I don't find that <- is an improvement over use. "<-" doesn't obviously translate to "use" - without looking at docs I would think it means something closer to "take", and use has the advantage that it's what react already uses.

That’s good feedback. I liked the look of <- at the beginning because it made the syntax stand out, and also was similar to a syntax someone proposed somewhere (can’t find it rn) that was like:

function MyComponent(props) {
  let counter << state(0);

  ...
}

I do think that there could be some sort of syntax that is more evocative of what Hooks do - hooking into the component’s render cycle and doing some sort of side effect - but the drawbacks of using <- in the function name is starting to grate. For instance, they show up as garbage in React DevTools.

I guess that begs the question: what should we use as the standard? Should we do camel case useXyzAbc? Or kebab-case use-xyz-abc? I’m kind of leaning towards keeping the React idiom in general. Hmm.