|
> Thank you for elaborating! I remember I was really curious when they introduced them, since there had been a lot of focus in the community on stateless functional components before, and the benefits of their purity. Now suddenly they were renamed to function components instead, and as you say, are no longer guaranteed to simply be a function of their props. Thank you for engaging thoughtfully and with some curiosity! I'm sorry it took me a while to come back to this, but I do want to answer because it's worth exploring. > What problems do you feel the current hooks API is causing, that could be solved with a different API? Frankly, once you introduce hooks, you can no longer trust that a function is a function. You can't call it without knowing whether it'll cause side effects. You have to know the hooks internals to know what those side effects actually do, and you have to know the internals of every component before you consider calling a component. It's a breaking change of huge consequence, with no type-level (whether you're using TypeScript or JSDoc or just web docs) indication of what a thing is. So now, allllll functions are not functions. A way a different API could solve this would be to make the construction and render explicitly separate (just like all of the pre-hooks state APIs for react). Modifying the example from the hooks intro: import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
--- return (
+++ return () => (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
This isn't where I'd dust off my hands and call it a day. A more ideal hooks API, with chaining and composition in mind, would not just produce free-floating values and state update functions. You could instead wrap props and essentially have a component-local API for a props/state combination where the component still is just a function of props: import React, { useState } from 'react';
function Example(props) ({
const setCount = useState(props, (props, state) => {
...props,
count: state,
}));
return (props) => (
<div>
<p>You clicked {props.count} times</p>
<button onClick={() => setCount(props.count + 1)}>
Click me
</button>
</div>
);
}
A comment adjacent to yours (which I don't have time or patience to respond to right now) asked how another API would deal with hooks which are instantiated per-render (which... I guess exists but it's mind-boggling because it's not how hooks are explained to the public), take either of these examples and nest them for each state/props case you'd encounter. This is the Python principle of explicit is better than implicit (which means, yeah, writing a bit more code might be a pain in the butt but you'll understand what's happening), and in FP is referred to as referential transparency ("If I call f(x) -> y, y will always be the same for the same x"). |
This API, which is one I came up with many years ago before hooks as well, is not hooks, and doesn’t have the same properties you’d want. It doesn’t at all allow composition. Honestly, it feels like you haven’t spent much time with hooks if this is your improvement. It's a great case of "making it look better but removing the valuable properties in the name of purity".
I already explained fairly well why you want hooks to be inline in render, as they need to handle side effects, they need to compose with each other and react to changing values, and therefore they need to constantly run to check for updates. It’s 100% how they are used by everyone.
I hope you find time to actually look at what hooks do more closely and find the power behind them.
Again - frontend always had highly stateful and state-changing components, they were just abstracts in ways that suited your tastes better before. But it is just taste you’re talking about, not some inherent superiority. You have to deal with state on the frontend that’s changing often during renders, and you want to handle it locally, and you need to compose and chain various units of logic. You can try and hide that fact behind more syntax, but it doesn’t change the fact that your inner function you defined is depending on an outer closure that is stateful, breaking your law of no functions that act differently when called with the same values.
Anyway, to each his own, if you don’t care enough to actually understand them and contend with what they solve then I won’t try and force you to!