URI:
       [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)