Remix.run Logo
AlexErrant 15 hours ago

Fair. My bitching would've been better expressed as "I groan every single time I attempt to violate a hook rule." Which is a lot, because I'm new to React. It's almost certainly a "skill issue", but hooks are NOT just "JavaScript functions", contrary to React marketing PR.

My conditional beef: in my app, users can choose between using the built-in mic for speech recognition or a 3rd party service (LiveKit). If the user chooses the built-in mic, I still must allocate memory for LiveKit's services because it's exposed as a hook, even if the user will never use it. This problem compounds - every option that I expose that uses a hook requires that I allocate memory for an option that may never be used. Also TTS - users can choose to use the phone's TTS, or a remote service, etc. Every option I offer, if the library exposes it as a hook (and they virtually always do), if I naively implement the feature, allocates memory for a hook that might never be used.

Fuck. React. Hooks.

My workaround is to conditionally render empty fragments. These fragments wrap hooks, which I then inject into the context. This makes it so I can conditionally run hooks. This is why I complained that React can handle conditional components, but not hooks. Concretely: https://pastebin.com/sjc3vXTd I'm using Zustand because god I need a lifecycle outside of React.

Y'know how people complain about how Async "colors" functions? Hooks are also a type of function coloring. And they don't compose with Async.

DangitBobby 14 hours ago | parent | next [-]

Yeah, this is a really annoying thing about how hooks work. For whatever reason (I'm sure they have a great reason) React can't do hook state book-keeping correctly without tying it to a function component lifecycle.

I think you actually can conditionally render a hook but that choice has to last for the entire rendered lifetime of the component. But that doesn't really help you when your user can switch between them.

Izkata 6 hours ago | parent [-]

Hooks can call other hooks, and all the built-in hooks rely on setState at the bottom. setState is state for the individual component. It keeps track of state across multiple calls with an indexed array - the first call is index 0, second call is index 1, and so on - that's why no key is needed to identify which setState call is which.

All the oddness about hooks fall out of that implementation. They can only be used inside components because they rely on component state management, and having to be called at the top level is a simplification of "hooks must always be called in the same order the same number of times" (no conditionals or loops) because otherwise the setState index gets messed up and you're getting the wrong state back when it's called.

nosefurhairdo 12 hours ago | parent | prev [-]

You don't have to use their hooks! Looking at your pastebin link, I would probably opt for something like a factory pattern instead: https://pastebin.com/PbnBqX4a

Just because you're in React land doesn't mean you can't still write regular old js/ts and hook in only when you need it. I imagine you'd do something quite similar in any other framework.

AlexErrant an hour ago | parent [-]

I appreciate your example.

Some libs do not expose "vanilla" js/ts functions that I can call - e.g. `LiveKitRoom` https://github.com/livekit/client-sdk-react-native

It only takes 1 hook to pollute your entire factory pattern; the comparison to colored async functions wasn't spurious. Hook-only options seem especially prevalent in the React Native ecosystem (ironic, given the memory constraints of phones).

Of course, I could fork/go down to the native layer, but this just proves my point that React DX is hot garbage.