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>;
}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.
(disclaimer: FP all the way, regardless)
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.
What doesn't work in JavaScript is functional programming.
https://rybicki.io/blog/2023/12/23/promises-arent-monads.htm...
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.
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
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.
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.
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.
Syntax makes sense and improves readability in business logic. Readability is good
Even HN has been taken over by shills from Big Mint.
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.
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>
);
}Local component state is great.
I use events for all other cross application communication.
Prop drilling is limited to one level - for making components that make children.
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
In fact, React was originally designed to be used this way (as the V in MVC)!
mjfisher•2h ago
- 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!