4 ms·
Show HN: Tired of logic in useEffect, I built a class-based React state manager
- mjfisher 6mo agoJust 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 6mo agoAll 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 6mo agoIt'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 6mo agoYeah, fetching data from a server in useEffect is a widely acknowledged and documented antipattern.
- xboxnolifes 6mo agoIt's a widely documented anti-pattern, while also not giving a convenient alternative.
- confidantlake 6mo agoIs it? I have never had any issues doing it this way.
- chrisweekly 6mo agoThis post explains it pretty well: https://dev.to/deveshsangwan/stop-using-useeffect-for-data-fetching-5hcl https://dev.to/deveshsangwan/stop-using-useeffect-for-data-f...
- confidantlake 6mo agoI am not convinced. Have been using this pattern for years without issue.
- chrisweekly 6mo agoYou can lead a horse to water...
- confidantlake 6mo agoI have been drinking water for years. You are trying to tell me water is an antipattern and I should drink gatorade instead. No thanks.
- chrisweekly 6mo ago
- hungryhobbit 6mo agoJavascript 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 6mo agoFor clarity, what do you call "classical OOP"? (disclaimer: FP all the way, regardless)
- hungryhobbit 6mo agoEssentially `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 6mo agoCan 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 6mo agoDon’t most OO languages have similar differences? My understanding is that even Java is quite different from, say, Smalltalk (which arguably is “more OG”).
- jemmyw 6mo agoWe have a similar style of react state manager that we use at Aha! https://github.com/aha-app/mvc 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 6mo agoSorry 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 6mo agoFar cleaner, how is testability though?
- SketchySeaBeast 6mo agoVery easy - mock the useNotifications and you can easily see all the behaviour by changing three properties.
- altbdoor 6mo agoWorking 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".
- dsego 6mo agoThat's a valid concern. I've seen some hard to grok hooks with polling, async stuff, hard to follow logic, etc. Like with anything, need to have taste, it's easy to dump too much into one hook and like you mention, it gets hard to follow what gets triggered when.
- deleted 6mo ago[deleted]
- oDot 6mo agoThe 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 6mo agoIs 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 6mo agoYes. In the Elm architecture it is possible (and ergonomic) to model your state separately from the view
- schneehertz 6mo agoVue provides a computed feature that acts as a buffer layer between the view and the state, so the view and the state are not necessarily strictly bound
- oDot 6mo agoComputed is only a concept because you need a band-aid for that lack of separation. You can read more about my efforts ditching Vue here: https://blog.nestful.app/s/the-tech-behind-nestful https://blog.nestful.app/s/the-tech-behind-nestful
- apatheticonion 6mo agoSame: https://github.com/alshdavid-public/mvvm/blob/main/examples/basic/index.tsx https://github.com/alshdavid-public/mvvm/blob/main/examples/...
- confidantlake 6mo agoSeems like a solution in search of a problem.
- 000ooo000 6mo agoI wish I'd never used current-day React as well
- fiddeert 6mo agoStill waiting for "I was tired of AI titles using the format 'I was tired of x, so I built y', so I built ..."
- lateforwork 6mo agoYou 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 https://github.com/Rajeev-K/mvc-router
- bobthepanda 6mo agoYou 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 6mo agoNot everyone likes hooks. Class components may have issues, but if hooks are the solution I'll stay with class components, thank you very much!
- bobthepanda 6mo agoI will say that I used to really not like hooks and that’s because I think the migration documentation and messaging was handled pretty poorly. That being said, if you’ve worked with state management like Redux, I would argue that hooks are pretty similar to how you hook up that kind of data store.
- lateforwork 6mo agoYeah, Redux is another big mistake I stayed away from. "Line after line of throat clearing and pseduostructure", as one developer put it [1] [1] https://news.ycombinator.com/item?id=15342764 https://news.ycombinator.com/item?id=15342764
- 000ooo000 6mo agoWhat exactly abut MobX do you find too magical/implicit?
- nextlsj 6mo ago[flagged]