Hacker News new | ask | show | jobs
by bern4444 2017 days ago
Hooks are the place to segment out effectful code from otherwise pure code

useRef is a hook to serve just that purpose as is useState and useEffect.

Building new hooks off of these base hooks lets you write your own custom hooks to handle whatever side effects you need.

The existence of a hook is what should alert you to the fact that there may be some side effect thing happening here.

Not sure if this applies to you but I've found that most people who criticize hooks haven't made the leap to using custom hooks and have instead only used ones provided to them (useState, useEffect, etc).

Hooks are an incredible way of encapsulating behavior, separating out that behavior from a component, and exposing that behavior to any component that needs the same behavior. Check out this video for a practical example: https://www.youtube.com/watch?v=nUzLlHFVXx0

I'm in the middle of a big refactor and writing reusable custom hooks are an absolute savior to share functionality across components that may have nothing to do with each other.

Hooks are functions, which is why we can compose them to build new behavior from smaller ones. That's the point. Just cause a thing is a function doesn't mean its pure. Like I said above, hooks are often meant to contain the impure parts of your otherwise declartive and pure UI expressing the UI as a function of state.

4 comments

I'm fully on Team Hook, and convert class-based components whenever I touch them. Still, I feel that there are some serious compromises in the hooks approach and wonder if there wasn't some other potential future we missed out on.

What would it have looked like if class-based components got a v2 rev instead? I feel like a lot of the crazy with class-based components came from the myriad low-level callbacks. useEffect() is a better abstraction, but it seems like some sort of class-based analogue could have been created (perhaps registering effects in the constructor).

The other nice thing about hooks is composability. I haven't given it a ton of thought but maybe that could have been addressed somehow too? Dunno.

I would be willing to accept some extra verbosity if it meant we could relax or eliminate some of the rules of hooks. The conditional rendering problem sucks.

> What would it have looked like if class-based components got a v2 rev instead? I feel like a lot of the crazy with class-based components came from the myriad low-level callbacks. useEffect() is a better abstraction, but it seems like some sort of class-based analogue could have been created (perhaps registering effects in the constructor).

This certainly seems like it would have been a better design, it would definitely be easier to reason about. Or even just an additional function boundary between where hooks are called and where JSX is rendered (semantically it’s basically the same thing but less verbose). Or even just changing the hooks interface to be a factory, accepting a component function as a parameter, where that component receives props `P & HookState<FooHook>`, which probably would be a lighter lift for React’s implementation because it could simply be a wrapper around the current behavior.

You could trial implement some of these ideas as a design pattern in the existing Hooks design as essentially an "HOC".

    interface HookClassComponent {
        render(): React.JSX
    }

    interface HookClassComponentConstructor {
        new(): HookClassComponent // all hooks should happen here
    }

    function renderHookClass(hookClass: HookClassComponentConstructor) {
        const instance = new hookClass()
        return hookClass.render()
    }
That said, if you were to actually try to build HookClassComponents like this you'd see some of why React presumably didn't bother to include an "HOC" like this. A lot of the hooks rely on simple arrow function closures around variables and it would presumably be a lot easier to make mistakes in handling that temporary mutable state directly as class (private) properties than in single function scopes, and there would presumably be a lot more pressure to make such state modifiable by outside components.

I'd be curious though to see more people try an "HOC" like this, though, and report back what sort of results they get.

I’ve thought about wrapping it as a proof of concept when I have some space to work on it. But I’m more inclined to try out a factory style interface (outer function initializes hooks and returns a function component that accepts the props for the base component plus state from the hooks).

I think it would fit more my preferred style as well as the kind of interface that people find attractive in hooks. It would work well with generics in TypeScript. And it wouldn’t be as fussy about the weird implicit behavior of component naming that came with wrapped/generated classes and decorators in past experiments with composition in past React fads.

  Hooks are the place to segment out effectful code from otherwise pure code
The effectful code shouldn't even be there. It should live separately from the render / view code to make it easier / faster to test without having to simulate a fake DOM. The average call to `render` takes around 100ms. The tests could be orders of magnitude faster and so much easier to test if people would just keep their business logic and effects seperate from their render code. Does no one remember MVC?
> The effectful code shouldn't even be there. It should live separately from the render / view code

If that's your preference React is probably the wrong tool to begin with. React is just the view in MVC. Plenty of frameworks exist to help build an MVC with react that provide their own structure and guidelines.

React is incredibly flexible and can be used in many ways. All this shows is the need for strong architecture decisions and guidance because React is so hands off in making recomendations of how it should be used or what tools it should be used with.

That's a strength. It absolutely requires more up front work on yours and mine part to construct an architecture that fits our needs, but it gives us the ability to completely tailor the solution which is the best part.

> > The effectful code shouldn't even be there. It should live separately from the render / view code

> If that's your preference React is probably the wrong tool to begin with.

That’s... a strange take, considering that’s how almost everyone wrote React UIs before hooks.

How is this and grandparent so heavily downvoted without proper replies? Seeing this a lot in this thread.

I feel like the unspoken difference in this thread is between people who have IO/business in their view code, and people whose taste is to strongly separate them, leaving component state for strict view modeling like animation timers, etc. I had this difference with my former tech lead.

I’m not sure which side of that you see me on. But to clarify, I’m definitely not a fan of mixing business logic/IO with view code, but I’m also not a fan of hooks for view modeling state either.

The problem I have with hooks is fundamentally that they perform magic side effects that change the semantics of both the call site and its return type: what would otherwise be a pure function of props returning a data structure (referential transparency) is transformed into a constructor/factory type thing that returns a stateful thing which will render differently for the same props when its internal state has changed.

I generally would prefer to avoid state, and model as many changes as possible with functional data flow, but even where I do need to reach for state I’d prefer its initialization be expressed entirely outside the render function body. It would be a very small API change to achieve the same expressiveness of hooks, with just a small bit of additional syntax.

What would you test with just a pure react component? What are you actually testing? Given some props it prints certain html?

I’d argue that test is not that useful. Instead what we do in the react world today is focus on integration tests. Testing what happens when you navigate to a page, click on elements, and make sure it does what it is supposed to.

The usefulness of testing a pure functional stateless component depends on the complexity of what the component does based on its props. And in some cases the testing is valuable but can be abstracted, e.g. with snapshot regression testing.

Just as you probably don’t get much value out of testing a function that increments an input number, but you may get a lot of value out of testing, say, a function that generates a series of smooth quadratic curves over a series of points.

And that’s a much easier thing to test at the unit level than integration.

It seems to me that either:

- your usage of props and rendering isn’t particularly complex, but your interaction model is

- you’re using state to manage that complexity where someone else may model it without state

I’m not passing judgment if it’s the latter, it’s fairly common in the frontend world to model that way. But it’s certainly not my preference.

If someone thought hooks were a bad idea, how and why would they ever "make the leap" to using them more than they have to?
fair point haha and that's certainly where my personal bias plays a role. When I see something I don't understand I like to look for the reasoning behind it. Some google searching, talking to coworkers, and a bit more googling usually sends me down the right path to finding more information

Clearly hooks are not a bad idea given how quickly they've caught on in the react/front end community too. So while the majority certainly isn't always right, there's usually a common reasoning worth understanding

> When I see something I don't understand I like to look for the reasoning behind it.

Similar view here, to an extent. But... with many topics, a majority of what I find is fanboy posts that strawman "problems" with earlier tech/tools, gloss over problems with the new way, and fawn over "hello world" examples. It can take a lot of time to get deep reasons, that may have some impactful value on your own problem spaces, and... at some point, I have to draw the line on "investigating all the bandwagons".

But also, at some point, one of the reasons to adopt something is "everyone else is", and there's some value in using "the latest trend" (hiring, bug solving, new tools, etc). And that may be enough value, but rarely does anyone justify their decision with "everyone else is doing it" - they try to give deeper 'technical' reasons, when (whether there are good/strong reasons) sometimes they just aren't able to do a good job.

The conclusion I came to is that some people are willing to put up with hooks if it means that they don't need to learn how classes and prototypes work. Maybe I'm wrong, but that's how it looks to me. Not all reasons are good reasons.
This seems at least part of it. And you don’t even really have to learn those things to use stateful React class components, they’re very seldom deeply inherited or require any `this` besides lifecycle methods and their own implementation. Rendered components are not instances.

I know there’s an aversion to classes in a lot of the current React community but the Component design in React was really much easier to reason with.

> Hooks are the place to segment out effectful code from otherwise pure code

I know what hooks are. Neither of those things is true. The hooks are called/instantiated in the same function body that returns the render. This performs some kind of stateful magic which makes that function itself inherently impure. Then that function returns a render which calls into that magic to perform further side effects, so that’s not pure either.

> The existence of a hook is what should alert you to the fact that there may be some side effect thing happening here.

That is true, and is for sure a benefit. But a class sends the same signal, but with the benefit of actually being fairly straightforward to reason about (notwithstanding the complexity of lifecycle, but lifecycle can be complex with hooks too).

> Not sure if this applies to you but I've found that most people who criticize hooks haven't made the leap to using custom hooks and have instead only used ones provided to them (useState, useEffect, etc).

I try not to use them at all, to minimize state and side effects as much as possible, and to encapsulate stateful code from rendering code.

I find them hard to reason about, whether in the core interface or as abstractions around the core. They fundamentally change the behavior of a function component from “this is a function which receives input (props) and returns a rendered data structure (JSX)” to “this sets up some initial state and returns a reference type of some kind that’s used to track that state over time”, but with the same syntax and semantics. The former is what attracted me to React and JSX, the latter is what I wanted to get away from. But making the same semantics do both is even worse than just having different semantics for both.

> Hooks are functions

Well not really, they’re routines expressed as functions, but they cause side effects on their enclosing context. That’s why, for example, you can’t conditionally call a hook. If you do, React won’t know to treat this component differently from components which don’t call hooks.