Hacker News new | ask | show | jobs
by ricardobeat 1574 days ago
> For example a flurry of setStates could be wrapped up in one single state. If it gets too complex - into a reducer

You've just deoptimized your app, and your whole component will re-render on every change.

> Components that don’t benefit much from splitting up could have their business logic wrapped into a context

You did it again. More unnecessary re-renders.

> it’s still performant and works

True that 'it works', but it's usually not as performant as you think - we just have really fast computers and phones now.

2 comments

>You've just deoptimized your app, and your whole component will re-render on every change.

If you have 3 useState each of those will use 3 useReducer internally (every hook is implemented on top of useReducer), If you consolidate them into one useReducer then you will end up with the same thing performance wise. Maybe even better. Whenever an event is pushed into the hook's queue it marks the component as dirty. The next call to useReducer will then reduce all unprocessed events into the current state. It's entirely possible that having less hooks and therefore less metadata in the background can improve performance more than avoiding the theoretical cost of rerendering a component that most likely would have to be rerendered anyway.

Not all hooks are implemented in terms of reducers. `useMemo`, `useCallback`, `useRef` for example are not based on reducers, (or effects). Obviously the effect hooks are based on effects not reducers, and some like ` useDeferredValue` are based on both.

But you certainly are correct that useState is reducer based. I'm pretty sure one is only avoiding rerender via using multiple `useState` if they don't implement the reducer in a way where it returns the original object when there was no net change. If you are able to implement the such that it only returns a new object when there really is a change, then a single useReducer call is strictly more efficient than multiple useStates. (This might require more complicated code, as returning a new object every time is often the easy way to implement reducers.)

Why is performance always argument No. 1? The true rule is, code must be readable and maintainable first. THEN, if there are performance issue with the code (no theoretical ones, you HAVE to have a real-world profiling report of YOUR code in your hands when you argue about performance), you can refactor for performance.
A thing I remind myself of regularly: Avoiding worrying about optimisation up front too much is not avoiding caring about optimisation at all, it's reserving the number of hours you can expend on optimisation until you have enough working to have a profile available, because you are inevitably going to be wrong about which part is actually slow until that point so your optimisation hours budget will be better spent with a profile in hand.

There's a parallel here to "no, you did not find a bug in the compiler". Yes, ok, once every five years or so I actually did find a bug in the compiler, but assuming you aren't that smart is still a far far better default approach.

That's a straw man argument - I did not say it's concern #1, just pointing out that React forces that specific trade-off. Balancing complexity, maintainability, AND performance is really hard with hooks, the lack of built-in reactivity and inefficient baseline behaviour. It's rarely a matter of "just combine it into one reducer".

On top of that, profiling and optimizing a real world application with a dozen hooks in every component is pretty painful.