[HN Gopher] Show HN: Tired of logic in useEffect, I built a clas...
___________________________________________________________________
Show HN: Tired of logic in useEffect, I built a class-based React
state manager
Author : thalesfp
Score : 21 points
Date : 2026-04-08 21:53 UTC (5 hours ago)
HTML web link (thales.me)
TEXT w3m dump (thales.me)
| mjfisher wrote:
| Just to sanity-check my reading of this:
|
| - Zustand exposes itself as a hook.
|
| - MobX does that observer-wrapper thing
|
| - Snapstate instead has an explicit writing step (`scoped()`) at
| the bottom of a component
|
| If so, I really quite like that. Kudos!
| igor47 wrote:
| All the examples are fetching data from a server, and in such
| cases I think tanstack query already does all the hard part. I
| feel like people under-use react query and put too much state in
| their FE. This might be relevant if your app has some really
| complicated interactions, but for most apps they should really be
| a function of _server_ , not client, state. Of course this exact
| reasoning is why I moved off react altogether and now use htmx in
| most of my projects
| dsego wrote:
| It's not just react query, you can make a quick useFetch and
| useMutation hooks (or claude can), it's not that complex. If
| you don't need more advanced features (eg caching), you can
| easily cut down on 3rd party dependencies.
| import { useState, useEffect } from "react";
| function useFetch(url) { const [data, setData] =
| useState(null); const [loading, setLoading] =
| useState(true); const [error, setError] =
| useState(null); useEffect(() => {
| const controller = new AbortController();
| fetch(url, { signal: controller.signal })
| .then((res) => res.json()) .then((json) => {
| console.log("Data:", json); setData(json);
| }) .catch((err) => { if (err.name
| !== "AbortError") { console.error("Fetch
| error:", err); setError(err);
| } }) .finally(() =>
| setLoading(false)); return () =>
| controller.abort(); }, [url]); return
| { data, loading, error }; }
| function App() { const { data, loading, error } =
| useFetch("https://jsonplaceholder.typicode.com/todos/1");
| if (loading) return <p>Loading...</p>; if (error)
| return <p>Error</p>; return
| <pre>{JSON.stringify(data, null, 2)}</pre>; }
| chrisweekly wrote:
| Yeah, fetching data from a server in useEffect is a widely
| acknowledged and documented antipattern.
| hungryhobbit wrote:
| Javascript and classes go together like toothpaste and orange
| juice. All good JS programmers I know essentially pretend that
| classes don't exist in the language (or if they use them, they
| only do so rarely, for very niche cases).
|
| JS does _not_ have classical OOP built in! It has Brandon Eich 's
| _prototypal inheritance system_ (which has some key differences),
| along with a 2015 addition to the language to _pretend it has
| OOP_ (but really that 's just lipstick on the underlying
| prototypal pig).
|
| If you use classes in JS, you're bound to be disappointed at some
| point when they don't behave like classical OOP. Most devs accept
| that and use more functional approaches (like factory functions)
| instead of OOP.
| epgui wrote:
| For clarity, what do you call "classical OOP"?
|
| (disclaimer: FP all the way, regardless)
| hungryhobbit wrote:
| Essentially `new Foo()`, where `Foo` can be a subclass of
| `Bar` that inherits properties in the same way we all learned
| in our Java (or whatever actual OOP) language.
|
| JavaScript gives you a class syntax that lets you make
| classes and extend them from each other, and for the most
| part they _will_ work the same way as a class from a language
| like Java ... but some things won 't.
|
| You can either become an expert on prototype chains, and how
| JS actually implements OOP (differently from Java) ... or you
| can just write factory functions instead of classes.
| kretaceous wrote:
| Can you give examples of how they are different? I've only
| done OOP in JS so I'm not aware of what I'm missing or
| what's supposed to be different.
| epgui wrote:
| Don't most OO languages have similar differences? My
| understanding is that even Java is quite different from,
| say, Smalltalk (which arguably is "more OG").
| lateforwork wrote:
| Encapsulation, inheritance and polymorphism all work fine with
| JavaScript classes. OOP works just fine.
|
| What doesn't work in JavaScript is functional programming.
| DauntingPear7 wrote:
| I don't see how? You can do all sorts of FP in js and it even
| has some of it in its built in APIs. .then comes from FP
| nurple wrote:
| Yes, but promises are (unfortunately) _not_ monads!
|
| https://rybicki.io/blog/2023/12/23/promises-arent-
| monads.htm...
| lateforwork wrote:
| You are right, there are some FP features in JavaScript.
| But the way it is used in the predominant UI framework
| (namely React) is definitely breaks FP, see
| https://mckoder.medium.com/why-react-is-not-
| functional-b1ed1...
| spankalee wrote:
| Counterpoint: classes are a great way to bundle state and logic
| - which is exactly what UI components are - and components
| models should use classes _more_ , not less.
|
| React's "functional" components are simply poor approximations
| of classes. Instead of easy to read and reason about class
| fields, you put state in useState() function classes that are
| effectively named by their appearance order in source and who's
| current state you can't introspect with dev tools!
|
| The component function mixes one-time setup (which should be a
| class constructor) with repeated render calls (which should be
| a method), so those of course have to be separated by putting
| the one-time work inside of a closure inside of the repeated
| should-have-been-a-callback function. Event listeners and other
| callbacks are another huge mess. Don't forget useMemo() or
| useCallback() (but which?).
|
| It's actually quite mad.
|
| And the differences between classical and prototypal
| inheritance basically don't even pop up under normal class
| usage in JS: just use class fields, don't mess with the
| prototype chain, and don't dynamically add or delete properties
| - all things that are how classical inheritance works - and
| things just work like you expect.
| Weebs wrote:
| They're modeling reactivity, not classes. It's a well
| established pattern in functional programming
|
| The one time setup mixed with repeated render calls is odd,
| but it's a design decision they made. It reduces boiler
| plate, though I don't necessarily agree with it because it is
| a leaky abstraction
| spankalee wrote:
| Reactivity and functional programming are orthogonal
| though.
|
| And in most functional systems the one-time setup function
| would return the render function so the render function can
| close over state.
|
| Which is pretty much what a class with fields, a
| constructor, and a render method give you.
| aylmao wrote:
| I think the biggest issue with classes is subclassing, it looks
| like a good feature to have, but ends up being a problem.
|
| If one avoids subclassing, I think classes can be quite useful
| as a tool to organize code and to "name" structures. In terms
| of performance, they offer some good optimizations (hidden
| class, optimized instantiation), not to mention using the
| memory profiler when all your objects are just instances of
| "Object" can be a huge pain.
| apatheticonion wrote:
| I have noticed that inheritance is largely ignored by
| experienced developers but it's a hard argument to make that
| "all good JS programmers do this".
|
| Classes are invaluable and are an extremely efficient and
| ergonomic way to manage state in GUI applications.
|
| That said, avoiding classes was published in some blog post at
| some point and the JS hype machine went crazy with FP. As a
| consequence, I have yet to observe a maintainable React
| codebase. Good looking and performant React applications are
| even fewer and farther between.
|
| Personally, writing idiomatic React has me focus too much on
| render cycles that I think less about how the application looks
| & feels. Appropriate abstractions become more difficult to
| conceptualize and any non-trivial application ends up a 5mb
| bundle with no multi-threading or optimizations. This is also
| what I have observed "the best JS devs" do in the wild.
| benatkin wrote:
| Inheritance is used here:
|
| https://github.com/thalesfp/snapstate/blob/ba8a8d7ce25d6a4ef.
| ..
|
| I'm not sure if it would support inheriting from a custom
| store very well. It might get tricky with the templating. But
| the author of this seems to have done a good job of not
| ignoring inheritance.
| apatheticonion wrote:
| Personally this library isn't to my taste - but I
| successfully use classes (without inheritance) along with
| reactivity primitives to create beautiful, tiny and high
| performance React applications
| molszanski wrote:
| Potato potato. Js classes are just closure sugar. So what?
|
| Syntax makes sense and improves readability in business logic.
| Readability is good
| hackingonempty wrote:
| > Javascript and classes go together like toothpaste and orange
| juice.
|
| Even HN has been taken over by shills from Big Mint.
| benatkin wrote:
| I went through the code, and it is roughly equivalent to doing
| it without classes. The nesting level in the example isn't deep
| - everything seems to be beneath a SnapStore or a
| SnapFormStore, without inheriting from user defined classes. I
| think the use of classes is fine, and the way that it's
| introduced in the blog post is good, it says "plain TypeScript
| classes". It is used as a means for ergonomically writing and
| understanding the code more than it is for setting up
| invariants or complex polymorphism.
| jemmyw wrote:
| We have a similar style of react state manager that we use at
| Aha! https://github.com/aha-app/mvc
|
| I think the intent is very similar even though there are some
| structural differences: move the state and state logic out of the
| view to classes.
| dsego wrote:
| Sorry for being pedantic, but the first example could be
| rewritten to extract the pattern into a higher level hook, eg
| useNotifications. One way to simplify components before reaching
| for store libraries. The reusable hook now contains all the state
| and effects and logic, and the component is more tidy.
| function Dashboard() { const { user } = useAuth();
| const {loading, error, notifications, undreadCount, markAsRead} =
| useNotifications(user); if (loading) return
| <Skeleton />; if (error) return <p>Failed to load:
| {error}</p>; return ( <div>
| <h1>Dashboard ({unreadCount} unread)</h1>
| <StatsCards stats={stats} /> <NotificationList
| items={notifications} onRead={markAsRead} /> </div>
| ); }
| whizzter wrote:
| Far cleaner, how is testability though?
| SketchySeaBeast wrote:
| Very easy - mock the useNotifications and you can easily see
| all the behaviour by changing three properties.
| altbdoor wrote:
| Working with multiple teams in a large project, hooks can be a
| nightmare to maintain.
|
| I see 7x layers deep of hooks, with no test cases to support
| them. Some of the side effects are not properly tested, and
| mocks that abstract away the whole implementation means the
| test case only works for certain scenarios.
|
| FWIW this scenario might be an outlier in large projects,
| considering how some developers prefer to "just wrap the hook
| in another hook, and not worry about its internals".
| oDot wrote:
| The problems OP tries to address are unfortunately a deep design
| flaw in mainstream frameworks like React and Vue. This is due to
| 2 properties they have:
|
| 1. They marry view hierarchy to state hierarchy
|
| 2. They make it very ergonomic to put state in components
|
| I've been through this endless times. There are significant ways
| to reduce this friction, but in the end there's a tight ceiling.
|
| This is why this kind of work feels like chasing a moving target.
| You always end up ruining something inherent to the framework in
| a pursuit to avoid the tons of footguns it's susceptible to.
|
| It's also why I moved to Gleam and Lustre (elm architecture) and
| bid those PITAs farewell
| svieira wrote:
| Is this because Elm forces you to separate the model
| computations from the view computations, which then lets you
| compose the model shape in one place and the view shape in the
| other, or some other property of the framework that I'm not
| aware of?
| oDot wrote:
| Yes. In the Elm architecture it is possible (and ergonomic)
| to model your state separately from the view
| apatheticonion wrote:
| Same: https://github.com/alshdavid-
| public/mvvm/blob/main/examples/...
| confidantlake wrote:
| Seems like a solution in search of a problem.
| 000ooo000 wrote:
| I wish I'd never used current-day React as well
| fiddeert wrote:
| Still waiting for "I was tired of AI titles using the format 'I
| was tired of x, so I built y', so I built ..."
| lateforwork wrote:
| You can use React in an MVC framework, with React used to
| implement the 'V' in MVC. You can use class components, and the
| code becomes extremely simple. Business logic is moved to 'M'
| layer (models) and the 'C' layer (controllers) coordinates
| everything. No hooks or other messy stuff needed.
|
| In fact, React was originally designed to be used this way (as
| the V in MVC)!
|
| See https://github.com/Rajeev-K/mvc-router
| bobthepanda wrote:
| You can do this pretty simply with hooks and reducers, where
| the reducer/dispatch are the model/controller.
|
| Class components have their own pitfalls when you start messing
| around with componentDidUpdate, which was part of the
| motivation behind functional components IIRC.
| lateforwork wrote:
| Not everyone likes hooks. Class components may have issues,
| but if hooks are the solution I'll stay with class
| components, thank you very much!
| 000ooo000 wrote:
| What exactly abut MobX do you find too magical/implicit?
___________________________________________________________________
(page generated 2026-04-09 03:00 UTC)