6 ms·
In React {Transitions} = F(state)
- dvtkrlbs 1y agoI dont think this is really true in practice. Most of the time there is context and 3rd party sources components get from. It is good idea to have your actual view use this paradigm though.
- dfabulich 1y ago> A React application can be thought of as modeling a state machine. Each render takes a state and produces the UI for that state. This is the famous UI = f(state) mental model of React. But, for complex applications, formally enumerating a transition table is often not feasible. When the number of possible states is not finite, a table will not suffice and we must instead define a mapping: a conceptual function which takes in a state and returns the set of valid transitions for that state. Not really, though. While you can model any program as a state machine, doing so typically adds nothing to your understanding of the program. State-machine diagrams are especially useless. A diagram is only useful if it has about a dozen things in it, maybe two dozen, maximum. But that doesn't mean you can model a dozen states in a state-machine diagram; in these diagrams, the transitions, the arrows between the states, are at least as important as the states themselves. So the diagrams are useless when the number of states + the number of transitions between them are greater than a few dozen. Typically that means state diagrams are only with a handful of states with a few transitions hanging off of each. But, if your problem is simple enough that you can represent it with a handful of states with two or three transitions each, then you have a very trivial problem, so trivial that the state-machine diagram likely added nothing at all to your understanding of it. This is why no popular UI frameworks actually do model UI as a set of states with transitions. It's easier to model the state diagram as a function and then just forget about the state diagram. That's what React is all about! A pure function, UI = f(state). It's better than a state diagram. This article is saying: "Hey, you could think of React, something conceptually simple, as something unnecessarily complicated, instead!" Gee, thanks?
- captbaritone 1y agoAuthor here. I think we might actually be in agreement. My point is that in React you don't formally describe the transition table because (and I think this is where we agree) that's infeasible for an app of any reasonable size. The observation I'm trying to capture in this post is that even though we don't define a formal transition table, we actually _do_ implicitly define the set of valid (user) transitions for each state via the event handlers we bind into the DOM when our React component tree renders that state.
- tshaddox 1y ago> But, if your problem is simple enough that you can represent it with a handful of states with two or three transitions each, then you have a very trivial problem, so trivial that the state-machine diagram likely added nothing at all to your understanding of it. And yet, it's extremely common to see apps with clearly broken states and state transitions for what should be relatively "trivial" state machines. Think play/pause buttons, buttons with loading states, form fields with error states, etc.
- revskill 1y agoThat is a valid way of thinking.
- mvdtnz 1y agoEvery time I delve into a large React codebases (and I have worked on some real monsters) I have a laugh to myself at how badly these guys tie themselves up in knots in order to preserve the supposed simplicity of the state -> UI function. When you invent something as insane as the hooks API in order to maintain purity it's time to step back and consider if you're really on the right track.
- paulddraper 1y agoHooks are the best thing for frontend dev since async/await.
- recursive 1y agoI would believe that if there were exactly two things in front end.
- LunaSea 1y agoI can't count how many issues I've seen due to: missing useEffect dependencies, cyclical hooks, unnecessary rerenders, etc. linked to this API. It has a lot of expressivity but is incredibly brittle and dangerous.
- paulddraper 1y agoThat’s true of every other GUI tech. Angular too many digest cycles anyone?
- taeric 1y agoI want to disagree with you, but I just can't. Every simple example on the different ideas for managing state in React is easy and seems reasonable. Every application I encounter seems to quickly take that and just start laughing at me.
- CharlieDigital 1y agoSomething with React's approach is fundamentally broken, IMO, and this is why we see so many variations of state management libraries on React that we don't see on other frameworks because state kind of "just works" and is really, really simple when you are using signals. My sense is that in React, the complexity comes from the management of minimizing the "blast radius" of state changes to prevent over-renders. So there are a lot of different approaches and ways that folks have cleverly engineered to solve this problem, but none of them feel as simple as say Pinia on Vue, for example.
- jschrf 1y agoThe vast majority of state problems in React are a result of nonsense cargo culting around the idea that classes are somehow bad.
- recursive 1y agoIMO it's the central tenet to the dogma that "state must not be mutable".
- geetee 1y agoThe absolute nightmares people create in order to attain this ideal...
- kccqzy 1y agoI'm glad I learned React during the time when React.createClass was the only way. It was simple and intuitive. At that time the docs included a very lengthy discussion of what should be put inside the class and what should be passed via props. It was very helpful when it comes to teaching newcomers to architect their app. It also carried over the years of intuition by the typical dev working with OOP. Much better than the current trend of using hooks for everything.
- blurker 1y agoI disagree. With classes, everything was stateful (because, y'know, classes). People were doing all sorts of crazy thing with the lifecycle methods and it was always a pain to have to remember the "this scope" and bind your event handlers. I saw so many bugs written by people who lost track of what "this" was. Both paradigms have foot guns but having used both I much prefer the hook version.
- tripplyons 1y agoI might just not be aware of a better alternative for React hooks, but I don't like useEffect. I feel like it makes it much more difficult to manage state and transitions compared to SolidJS or other frameworks that use signals.
- alpinisme 1y agoThere’s no denying that the idiomatic solution is sometimes far from obvious, but idiomatic react wants useEffect to only about synchronizing react with external systems. Everyone reaches for it to synchronize between components and do all sorts of other non-idiomatic things though, and that’s where the pain comes in.
- LegionMammal978 1y agoOut of curiosity, I just looked through some of my old code calling useEffect. Most of it was for fetching data from an API on mount; I'd also written a custom little hook that returns a callback to signal that the data should be refreshed. But a few instances were to conditionally set one piece of state whenever another piece of state was changed, arguably an abuse of the mechanism. I suppose the proper way would be to wrap the setter function into one that changes both, but it takes a fair bit of discipline to avoid useEffect in the heat of the moment.
- wk_end 1y agoThere’s an excellent article in the React documentation about this (“You Might Not Need An Effect”). Back when I was working on a React team I probably threw it at a code review on average once a week. I really like React, but given the way developers seem to struggle to use it “correctly” (despite all the lint hooks and weird diagnostics like double rendering to help) it’s hard not to feel like there’s something wrong with it.
- marksomnian 1y ago> But a few instances were to conditionally set one piece of state whenever another piece of state was changed That use case is explicitly called out on the "You Might Not Need An Effect" article in the docs (which everyone writing React should read, and arguably was published years too late): https://react.dev/learn/you-might-not-need-an-effect https://react.dev/learn/you-might-not-need-an-effect TLDR: When updating a useState based on another useState, don't use the first useState at all, just compute it inline. If it's expensive, wrap it in a useMemo. When updating a useState based on props, call the setter directly from the component function, and React will immediately re-render the component (instead of rendering it, running the effect, and then rendering it again).
- kccqzy 1y agoThe most direct way of solving the immediate problem is to make transitions idempotent. Why must it be an error to complete an already complete TODO? Completing an already complete TODO should be a no-op. That simplifies things greatly. Of course many actions logically cannot be made idempotent, but for the subset that can, do it as much as possible.
- a_wild_dandan 1y agoExposing invalid transitions to a user is a bug. Idempotency here doesn't solve anything, just hides said bug, which is arguably worse.
- antonvs 1y agoA race condition for which of multiple concurrent users initiated a transition is not a bug, it's a scenario that commonly needs to be handled. In many cases, idempotency can be a simple and effective approach for handling this.
- veidelis 1y ago"This is the famous UI = f(state) mental model of React". Famously incorrect generalization. Why? For example, the useRef hook enables components to hold their own state. React components are not guaranteed to be pure functions. Of course, it can depend on how one writes their code, but it's not a guaranteed that UI = f(state) in React in general.
- guhidalg 1y agoIt's a mental model, not how it works. Your computer isn't actually executing C code, but it's helpful to think that it does. If you write React code that strays from that model, you better know what you're doing. When I have to reach for `useRef`, I know that I'm in dangerous water.
- ketzo 1y ago"All mental models are incorrect; some mental models are useful"
- 10000truths 1y agoThis is needlessly pedantic. useRef/useEffect are tools to implement the model on top of an imperative reality. Things like canvas rendering APIs don't have a pure interface, but it's still obviously very useful to provide one (hence libraries like react-konva and react-three-fiber).
- bob1029 1y agoThe #1 reason I push for SSR/vanilla web is to consolidate all state to the server. Literally the only thing on the client could be a session cookie. A few hundred bits of entropy. That's the client's state. Imagine how simple that would be. The cost of a round trip for every UI interaction might seem high, but I've never seen a distributed client/server state machine model that could compensate for any of these alleged UX harms without simultaneously bringing in more complexity than anyone was prepared to deal with.
- adamddev1 1y agoBut then we lose the ability to do anything offline. Offline web apps are still valuable. Some people want to turn their data off sometimes. Many people live in places where internet access is spotty. I also love the simplicity of SSR/vanilla web for some things. But I say keep offline-first SPA/PWAs alive. Cross-plaftorm. Freedom from the app stores. Freedom from needing to be tied online all the time.
- LegionMammal978 1y agoYeah, sites demanding a roundtrip for every small interaction can be a pain to use when traveling. The principle I'd wish more web devs would keep in mind is, "Just because the client managed to contact your server once doesn't mean it will have fast and easy access to it in perpetuity." Indeed, in my experience, too many roundtrips is the cause of most atrociously slow websites, regardless of how heavy or light a framework they use.
- adamddev1 1y agoWhen you have a website that needs to be always accessed online, absolutely, give us an old-fashioned SSR vanilla web experience. Just give us a page WITH ALL THE DATA that loads in half a second. Don't make us wait for 5 seconds with spinners and placeholders while you make the client fetch data itself from different sources. This is insanity and torture! People are using client side rendering for the wrong things. But there are good and powerful use cases for client side rendering.
- nkrisc 1y ago
- whalesalad 1y agoWhy does it feel like React (not just the lib but the community/ecosystem/everything) took something as straightforward and easy to understand as functional programming and surrounded it with so much fluffy pomp and circumstance that it is unrecognizable?
- ketzo 1y agoThe answer -- as it is with every version of "why is this JS so complicated?" -- is that frontend web dev: - is the default way that most humans interact with computers - is an extraordinarily broad problem space and solution space - is something that basically every modern software company needs to do in some capacity, and with even a few developers, abstractions become desirable I'm not saying there wasn't a better way for React to adopt FP principles. I certainly have my own gripes. But to start a conversation with "why does the most widely adopted framework for the most broadly-used software interface in history have some rough edges?" seems, to me, to be sort of begging the question.
- hombre_fatal 1y agoBecause (1) UI app development isn't simple, so there are many ways to cook the egg, and (2) React isn't opinionated, so there are a lot of competing options, and it has quite a large surface area of use-cases.
- the_gipsy 1y agoI've never seen a Teact project where UI = f(state). There is always heavy reliance on the "lifecycle" of components. So you compose "functions", but every function is using some global state, shared or not, it's meaningless to describe it as "functions". The one project I've used that had redux was also a complete nightmare to work with, and hooks are the blessed way now apparently.
- kccqzy 1y agoIt doesn't have to be this way for most apps. One reason I've seen why people heavily rely on the lifecycle methods is that UI = f(state) isn't fast enough. But in more powerful languages like ClojureScript, you can keep track of which part of the state is accessed, and subsequently rerun only parts of the f that needs to rerun. As I understand, there is something called React compiler (https://github.com/facebook/react/tree/main/compiler https://github.com/facebook/react/tree/main/compiler) that tries to do this. But in a better language, this doesn't need to be a standalone tool, just a macro.
- the_gipsy 1y agoI agree, Elm also does UI=f(s) really nicely. But it's not something you can do in JavaScript while also keeping everything JavaScript.
- AstroBen 1y agoThe problem is most react developers are using the library because they want a job and see companies are using it, not because they believe in or even understand functional programming The amount of times I've heard that classes = OOP and functions = functional is ridiculous