23 ms·
Things I wish I knew about state management when I started writing React apps
- thereyougo 7y agoOP comment on Reddit: >https://old.reddit.com/r/javascript/comments/f1jop9/6_things_i_wished_i_knew_about_state_management/fh6oi33/ https://old.reddit.com/r/javascript/comments/f1jop9/6_things...
- k__ 7y agoI don't even use any of these state libraries. All logic in the page/screen components and the prop-drill them down to where they are displayed.
- veeralpatel979 7y agoAuthor here. I see the appeal of the simplicity of this approach, and this is how I handled state in my application before I knew what state management was. But how do you deal with the problem of many of the components in your tree needing to know what theme is set?
- k__ 7y agoI used CSS frameworks for theming.
- veeralpatel979 7y agoOk; ignore that specific example -- if you needed to share a piece of state among many components in different places in your component tree, how would you do it?
- k__ 7y agoI have 3 tiers of components. Root - Page/Screen - UI Elements The Root takes care of authorization and navigation logic, to switch between pages. A Page has all the logic needed for one page it holds all the state and passes it down via props. A UI Element displays the state it got via props and sends up its events via handler function (it also gets them via props) In 90% if cases this was enough. Sometimes, I needed to pass some state from the Page up to the Root and from there down to another Page after navigation, but that was it.
- veeralpatel979 7y agoGot it. If you don't have any data that's shared between pages, I can see how this works.
- nubb 7y agoI’ve been learning React and have been avoiding Redux out of fear of complexity. However, as my app grows more complex, the need for a global state manager becomes more apparent. Time to start learning Redux today :)
- leetrout 7y agoOr mobx :)
- shakopeeant 7y agogross
- reflectiv 7y agoCheck out mobx-state-tree ...it's mobx on steroids, and it provides what is missing from mobx to make it a state management solution for large react apps (minus the boilerplate like you get with things like Redux).
- mondaygreens 7y agoTry zustand!
- breytex 7y agohttps://github.com/react-spring/zustand https://github.com/react-spring/zustand
- brlewis 7y agoIt's not the amount of complexity that leads to needing Redux, it's the type of complexity. You can go a long way on keeping state in the nearest common ancestor of the components that need to share it. https://redux.js.org/faq/general/#when-should-i-use-redux https://redux.js.org/faq/general/#when-should-i-use-redux
- tempsy 7y agoI never understood why redux was considered "hard" - it basically keeps track of a plain javascript object that you can read/write across a react app
- ellius 7y agoBounced around 2 projects in the last 12 months, one Angular and one React, that are using the Flux pattern. It is a real bitch to unit test anything but the individual pieces, which ends up being kind of meaningless.
- wolco 7y agoPlease explain the flux pattern?
- acemarke 7y agoSee the docs for the original Flux Architecture: https://facebook.github.io/flux/docs/in-depth-overview https://facebook.github.io/flux/docs/in-depth-overview Redux was originally designed as a Flux implementation: https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/#the-intent-and-design-of-redux https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-ta...
- interlocutor 7y agoFrom that Facebook article: Flux eschews MVC in favor of a unidirectional data flow. This is so incorrect. MVC doesn't imply two-way data binding. The whole Flux architecture was based on a misunderstanding of MVC, and is therefore questionable. Most UI frameworks, including iOS, ASP.NET Core, JSP and JSF (Java based frameworks), Ruby on Rails, and Django (Python) are all based on MVC. Why is it that MVC works for all these other frameworks, but doesn't for React? The answer is that MVC does in fact work for React. React itself originally used the tagline "React is the V in MVC". Flux/Redux introduce needless complexity and unnecessary boilerplate. There are MVC libs that make React much easier to use.
- RookyNumbas 7y ago> iOS, ASP.NET Core, JSP and JSF (Java based frameworks), Ruby on Rails, and Django (Python) are all based on MVC. Don't all of those have two-way data binding? > Why is it that MVC works for all these other frameworks, but doesn't for React? I think the point is that Facebook found it doesn't actually work well, at least for their use cases. They found a recurring set of bugs caused by two-way data flow, and built a framework to alleviate it.
- vga805 7y agoWhat's the deal with the recommendation to use some library for managing forms? I've been working with React almost since its initial release and I've built some pretty complex forms... yet form libraries remain the one thing I've never really seen a need for. At the very least, using something like formik shouldn't be a necessity. There should be some qualifier that it's only really needed for very complex forms. Or am I missing something?
- couchand 7y agoI've spent a lot more time fighting with formik than I've ever spent just working with inputs directly in React.
- latchkey 7y agoYou certainly do not need to use a form library, but it does make it easier to deal with complicated issues such as render counts. I built a thin wrapper around material-ui and react final form (what I would suggest over formik) [1] and a little demo [2] that visually demonstrates this. [1] https://github.com/lookfirst/mui-rff https://github.com/lookfirst/mui-rff [2] https://lookfirst.github.io/mui-rff/ https://lookfirst.github.io/mui-rff/
- breytex 7y agoI felt exactly the same for many years, but when hooks where introduced, I discovered react-hook-form[1]. It just blew my mind how you can write complex forms with validations in just a few lines of code. Give it a try. [1] https://github.com/react-hook-form/react-hook-form https://github.com/react-hook-form/react-hook-form
- adamzapasnik 7y agoI've been looking for something like that, thanks for recommendation, gonna use it on the next occasion.
- bluebill1049 7y agoThanks very much for sharing ️
- SlowRobotAhead 7y agoThis is one area I have to give a huge tip of the hat to Flutter and Provider, BLoC pattern using streams (and transform streams!) and RX generally. It's been a lot easier to comprehend coming from C than the React options.
- brlewis 7y agoPoint 1 in this article is totally misguided. "State management is how you mitigate prop drilling" implies that when prop drilling is not a problem (i.e. most of the time), that you don't need to think about state management. Even if you're prop drilling, you need to make sure data is not stored/updated in multiple places in your app in order to keep the UI consistent. The one redeeming part of that section of this article is that it links to another article that isn't as wrong. The linked article details when prop drilling is and isn't a problem. And most of the problems it describes can be reduced by using TypeScript. https://kentcdodds.com/blog/prop-drilling https://kentcdodds.com/blog/prop-drilling
- brlewis 7y agoIn fact, the linked article is useful because it links to a much better article on state management with React: https://kentcdodds.com/blog/application-state-management-with-react https://kentcdodds.com/blog/application-state-management-wit...
- veeralpatel979 7y agoHey - author here. I did not mean to imply that state management is only useful to mitigate the prop drilling problem. In fact, in #2, I give another example where state management is useful.
- brlewis 7y agoThe first section reads as if it's explaining why state management is needed at all, not as if it's giving one example of something state management is good for. I couldn't make sense of the second section either. It sounds like it's recommending a global store as the solution to updating a single component. The correct state management for a single component is what comes with React.
- the_optimist 7y agoSee also: Reagent. State is a first-class construct in Clojure/script, and forms associated with statefulness and state management are directly available. From that perspective, none of the front-ends presented appear complex. https://github.com/reagent-project/reagent https://github.com/reagent-project/reagent
- St_Alfonzo 7y agoFor more complex state handling in SPAs (subscription, event handling) re-frame is a really great framework based on reagent/cljs. https://github.com/day8/re-frame https://github.com/day8/re-frame
- qwerty456127 7y ago> Think about the most complex frontends you’ve used. Frontends that made you wonder — “how did they create this”? What makes these frontends complex? State management. The frontend “knows” a lot of things, and these things interact with each other in non-trivial ways. So in my view, state management is the core problem when developing a UI. To build a non-trivial React application, you need to consider state management, in my view. For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color. This makes me wonder “how did we allow this to happen”? The beginning of this article alone reflects the whole tragedy of modern programming. When I was a child we would just use common controls and the operating system would paint them whatever way configured in the control panel. Now we have to solve a complex state management problem just to implement a dark mode. Application programming was supposed to become simpler and easier, not this...
- deleted 7y ago[deleted]
- breytex 7y agoYou got that wrong. Modern JavaScript / SPAs make it possible to implement something like Outlook right within your browser. No (big) download, no clicking through an installation wizard, no fragmentation of versions for a tool, which forces you to be online anyway. I don't get all the hate towards SPAs and JavaScript Frameworks here on HN. Everyone is basically complaining about complexity for applications, which are more than just a server-rendered form or message board (which is a way less complex applications compared to outlook or slack, where "realtime notification" is a requirement). Application programming became easier by a lot, just the application requirements grew at the same speed, resulting in that feeling of stagnation. With something like zustand[1] or the react context api, its just ~10 lines of code to store the dark-mode boolean somewhere central in the app and connect it to all the components without prop drilling. [1] https://github.com/react-spring/zustand https://github.com/react-spring/zustand
- goatlover 7y agoBut is React simpler for building UIs than the RAD tooling of Visual Basic, Delphi or the Flash Designer? Obviously there is the advantage of the web platform to leverage with JS frameworks, but there was something really nice about being able to drag and drop standard controls that all users were familiar with, and then just attach some code to them that talked to a database or whatever. I think that was the Parent's point that modern web development, while having the advantages of the web, is still more complicated than what was popular a decade or two ago for making UIs.
- brlewis 7y agoPoint 2 in this article says a global store is the solution to its example problem. If there are any details of the example problem that make a global store the best solution, those details are not given. Generally you store a component's state in the component, and store shared state in a common ancestor of the components that share it.
- mfrye0 7y agoYeah, he didn't do the best job of explaining why in that example. Using a global store makes sense when your todo list evolves and the component storing the data is no longer mounted / exists. Examples could be: - Full page individual todo edit - Separate component / page that uses the same data - Some background logic that does X based on the todo data
- rhacker 7y agoI think the largest myth that exists in the react world is the need to have a global state for everything. I've seen too many failures when teams go from displaying "this one thing" to suddenly have the requirements to display two or more of that at the same time. Redux global state for the user that is logged in? that's OK, but do you really need Redux for that? Our team uses hooks and context for everything, and a little bit of legacy redux usage.
- acemarke 7y agoFWIW, we have some rules of thumb in the Redux docs for when it _might_ make sense to put data in the Redux store: https://redux.js.org/faq/organizing-state#do-i-have-to-put-all-my-state-into-redux-should-i-ever-use-reacts-setstate https://redux.js.org/faq/organizing-state#do-i-have-to-put-a... We're currently working on a major rewrite of the Redux core docs, and will emphasize this thought process in more areas of the docs as we go.
- Supermancho 7y ago> I think the largest myth that exists in the react world is the need to have a global state for everything. Client state management is all global no matter the strategy. ie Component level stores are effectively namespacing a global space with more cruft. When you get into sets of components, the convenient store gets ugly/brittle no matter how you do it, so might as well keep it in a single store.
- veeralpatel979 7y agoHey! Author here. You don't need to put the username of the logged in user into Redux, but if multiple components across different pages need to access this data, you should make it globally accessible. You can choose Redux/MobX for this, or you can roll your own solution with context and hooks. I'm actually learning more about the context/hooks approach myself. Kent C Dodd wrote a great article on this topic I'd recommend: https://kentcdodds.com/blog/application-state-management-with-react/ https://kentcdodds.com/blog/application-state-management-wit...
- Waterluvian 7y agoI'm starting to lean towards holding ALL app state in a single store rather than having component local state because every so often I get burned by not seeing the future. Example: okay I have a bunch of tabs that show different views. The state for which tab is selected can live with the tab container. Months later I find that I need other parts of the app to be able to switch to a different tab when user actions happen. At this stage all I put in local state are the most basic things like dropdown open/closed state.
- jamil7 7y agoThis is the way Elm works and I believe some people used or advocated for using Redux in this way early on. It never really caught on in React apps in my experience but it makes some interesting debugging methods easy to implement.
- miglmj 7y agoThis is also how it works in Clojurescript (through Reagent/Re-frame). I don't have too much JS react experience, but I often read this kind of article and marvel at how huge the mental load is working on react projects in native JS.
- vga805 7y agoYou use a global db atom in re-frame for global state, but there's nothing stopping you from using a reagent atom local to a component, is there? I never thought of the existence of the global db as the thing that ALL state should go in., or is that a community best practice? Edit: after digging through the re-frame docs themselves, you can see here that local reagent atoms are used for local state even with the global app db: https://github.com/day8/re-frame/blob/69a3fcd411369fff65404e7d6a924ce1968e2dba/examples/todomvc/src/todomvc/views.cljs https://github.com/day8/re-frame/blob/69a3fcd411369fff65404e...
- nightski 7y agoI never find myself "burned" by locally stored state because it's trivial to move state up the tree and pass it down as props. But boy does it simplify an application a lot using locally stored state where it makes sense.
- adamkl 7y agoTo manage state in our React application, we have a "service layer", just like you’d see in a server side application, only these are based on state machines (reactive services based on RxJS observables works as well). We also created associated custom hooks that allow developers to easily "inject" a service into their React component (we use React's Context API for our "DI container"). From there developers can read the service state to drive the look of the UI, or send events to it to trigger behaviour that is encapsulated in the service itself. Our most used service is a UserSession service that manages OAuth based login flow and session management. The best part of this approach is that our service layer started in an Angular based proof-of-concept before we lifted-and-shifted it all into React. [1] https://xstate.js.org/ https://xstate.js.org/
- aphextron 7y agoAre you guys using xState entirely as your data store, or using something else in conjunction like MobX or Redux?
- adamkl 7y agoJust xState. Each machine is responsible it’s own piece of the global state pie by using its internal `context`. This allows a similar approach to redux’s actions and reducers (or events and `assign` for xState), but each machine can focus on its area of responsibility and enforce its behaviour through defined state transitions. This approach also makes it easy to coordinate between multiple services (e.g. having the “NavigationService” change which links should be available by listening to a state change on the “UserSession” service as a user becomes authenticated. This started as a bit of an experiment to see what a stateful UI “service layer” might look like, but as we’ve progressed, we haven’t found a need for anything else like redux or mobx.
- UberMouse 7y ago> e.g. having the “NavigationService” change which links should be available by listening to a state change on the “UserSession” service as a user becomes authenticated. Could you expand on how you're achieving this? I'm creating a new project using xState at work and this is one of the things I've been trying to come up with a solution too. My biggest problem is the pattern I've set up for routing on top of xState (It's an electron app so no URL management required which makes that easy) requires you to create the state machine yourself, so it can't for example be used as an actor in part of a larger system. It sounds like your NavigationService and UserSession service might be similarly separated so I'm curious how they're communicating? Something that occurred to me last night is that I can probably make a coordinator of some sort somewhere in the tree which forwards actions from state machine to another as required.
- mfrye0 7y agoFor anyone not familiar with UI app design, you do not need to always default to a Redux or Mobx. I've found a simple object / class with an event emitter will suffice for many cases.
- crtlaltdel 7y agoyeah i used this for a while https://www.npmjs.com/package/tiny-state-manager https://www.npmjs.com/package/tiny-state-manager
- the_gastropod 7y agoI'm one of those "old dogs" who don't quite get the advantage of using React in the majority of the cases it's used. This article didn't really help... > What makes these frontends complex? State management. The frontend “knows” a lot of things, and these things interact with each other in non-trivial ways [...] Example: dark mode support. For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color. Back in my day.... you'd slap a class on the <body> tag, and you'd have all you'd need for your CSS to style the rest of the page accordingly. How is using Redux an improvement here? None of the examples of "complex" front-ends would be at all difficult in an old-school "just re-render the page" setup using something like pjax or turbolinks. And in my experience (seeing web pages with > 1MB js files) it'd be faster/more responsive for the end-user too.
- jfengel 7y agoReact and Redux are for apps. If you can do it with HTML/CSS, you absolutely should. React/Redux are a replacement for what people used to do with jquery or plain JS. They're not for display; they're for complex interaction of the kind that used to require a native user interface (Java Swing, Gtk, Win32, etc). Javascript is a mess, and the DOM is a mess; React makes that slightly less awful. (Redux is an extension to React to help manage global state; again, it's compensating for just how bad Javascript is.) Bonus: React encourages the use of JSX, a language that makes a lot of behavior look as if you were writing it directly in HTML. You don't need React to use JSX, and you don't need JSX to use React, but they do dovetail nicely. You can write in plain JS/jquery if you want. Plenty of people do. React is a framework to make that a bit easier; that's all.
- the_gastropod 7y agoI actually write a good bit of React professionally. I'm familiar. I just have yet to see a very compelling case. And the excuse is almost always this nebulous "rich/complex interaction". And.... I just rarely see any good examples of this. The huge majority of apps (web or otherwise) are not video games. They're forms that submit stuff to a server and get a response. There's sometimes a dropdown here or there.... and that's it.
- disease 7y ago> URL state: the route the user is on now. Read and update window.location; don’t create a second source of truth I've seen so much back and forth on this issue during my time developing with React. Intuitively, it seems like the URL should be treated the same way that DOM elements are: as a source of actions that may or may not result in state change - in addition to a reflection of state.
- reggieband 7y ago> but you do need to figure out how to store global state that can be accessed anywhere in your application. Immediately I disagree and feel antagonistic towards this article. Then the author provides an example of "prop drilling". Sounds an awful lot like Inversion of Control [1]. I've been down this road before. Next comes a Services pattern [2] (the world re-invents COM once again) and then comes Dependency Injection [3]. What he calls "global state" others have called "cross-cutting concerns" [4]. Pretty soon we'll be all aspect-oriented-programming [5] up in here. > What you need to do is store your theme setting in a Redux or MobX store, or in a plain JavaScript object, and pass it to all your components using Context. Yup. The beginning of a services pattern by changing to inversion of control. Once you realize you don't want all of your state on a single object you'll have `context.getUser` and `context.getTheme` and then you'll think having specific functions is a pain so you'll have `context.get(ContextType.User)`. Then you'll have interfaces so you can easily unit test with mock services. This is all good stuff. Keep going down that road. 1. https://en.wikipedia.org/wiki/Inversion_of_control https://en.wikipedia.org/wiki/Inversion_of_control 2. https://en.wikipedia.org/wiki/Service_layer_pattern https://en.wikipedia.org/wiki/Service_layer_pattern 3. https://en.wikipedia.org/wiki/Dependency_injection https://en.wikipedia.org/wiki/Dependency_injection 4. https://en.wikipedia.org/wiki/Cross-cutting_concern https://en.wikipedia.org/wiki/Cross-cutting_concern 5. https://en.wikipedia.org/wiki/Aspect-oriented_software_development https://en.wikipedia.org/wiki/Aspect-oriented_software_devel...
- draw_down 7y agoNo, none of this has anything to do with dependency injection or AOP
- john-radio 7y agoHelp me out here. So state management refers to frontend tools like Redux and Vuex, and it sort of represents the "backend of the frontend" in that it defines constructs and their behaviors for the frontend to manipulate. Is that a correct understanding? If so then doesn't it result in some bad code duplication between e.g. the Redux layer and the Django models?
- zelly 7y agoYes, that's why a lot of people who do SPAs also run node as the backend so that business logic code can be shared. If you have an e.g. C++ backend, you will have to rewrite your models in JavaScript and keep them in sync. It makes your website have the same development tedium as a mobile app but without any of the prestige or profit potential. Avoid SPAs at all costs. More companies are starting to ditch them and only have websites as advertisements for the native mobile app (CashApp and Venmo off the top of my head).
- mikewhy 7y agoYou deal with the same model interfaces, just as you would in any client / server pattern. But that's usually the extent of it.
- draw_down 7y agoI think it's best to either have some sort of codegen that creates the frontend types from the server-side types, or work in the same language so they can be shared. You're right that that sort of duplication is a risk, it should be addressed with tooling (or, again, doing everything in JS). As with everything else, there should be a single source of truth.
- williamdclt 7y agoThat's pretty much it. The Redux store is a database (up to you to keep it normalized), things like selectors and actions are akeen to controllers (one just reads data, the other just writes). You do have some "concept duplication", more than code duplication. Indeed you'll have to define your interfaces twice (Django models and Typescript definitions for example), but that's always the case if you consume an API (you always hope that what you expect from an API is what is received).
- brlewis 7y agoIn other comments I've pointed out specific major flaws in this article, but for whatever reason it continues to be upvoted and has risen to number 6. To be clear, this article is not useful. If you are trying to learn about state management in React, do not read it. Read this one: https://kentcdodds.com/blog/application-state-management-with-react https://kentcdodds.com/blog/application-state-management-wit...
- soneca 7y agoOk, only tangentially related. I would appreciate anyone more knowledgeable than me in React chipping in: Why Redux is the norm for React state management? Follow-up specific question: In which cases is it better than react-easy-state[0]? I am using react-easy-state on my company and on my side-project web apps. For me at least, it seems to add much less cognitive overhead than Redux. It is simpler to understand, simpler to use, simpler to read and maintain. Am I missing anything? Because react-easy-state seems very underrated in my opinion. [0] https://github.com/RisingStack/react-easy-state https://github.com/RisingStack/react-easy-state
- pier25 7y agoI don't know but my guess is because Redux was released a couple of years earlier and also because its author is in the React team.
- acemarke 7y agoI'm a Redux maintainer. There's a bunch of reasons why Redux took off: - In 2014, a year after introducing React, Facebook announced the "Flux Architecture" concept [0]. Since it was mostly just a pattern and not a library, over the next year the community created dozens of competing Flux-style libraries, which I've referred to as "The Flux Wars" [1]. - Redux came out in the middle of 2015. Its design took inspiration from many of the existing Flux libraries, but then added some Functional Programming principles on top of that [2]. Many React devs concluded that Redux was the better Flux implementation, and several of the other Flux library authors promptly stopped working on their libs and began recommending Redux. - Redux was designed to work well with React from the beginning, including the design of the React-Redux API [3]. Among other things, React-Redux made use of what was then a little-known React feature called "context", which enabled users to avoid prop-drilling values all the way down the component tree. In addition, the initial sales pitch concepts of "time-travel debugging" and "predictable state updates" appealed to many developers who had seen problems with libraries like Angular and Backbone. - Along with all that, Dan Abramov had already picked up a bit of a reputation in the React community from his work on things like React-DND and some Flux-related blog posts. Once Redux hit an initial critical mass, it became self-sustaining. People in the community assumed that if you were going to use React, you _had_ to use Redux. Tutorials were written that taught both of them together (along with setting up Webpack and Babel from scratch). Now you've got bootcamps teaching beginners React and Redux at the same time (which is unfortunate, because we recommend that people should focus on learning React first, and only tackle Redux once they're already comfortable with React). Today, there's certainly plenty of other good options out there, with varying tradeoffs. My own estimates are that around 50% of all React apps do use Redux, and overall absolute usage is continuing to grow [4]. More recently, our new official Redux Toolkit package has been designed to simplify many common Redux usage patterns, and is now our recommended approach for writing Redux logic [5]. [0] https://facebook.github.io/flux/docs/overview/ https://facebook.github.io/flux/docs/overview/ [1] https://blog.isquaredsoftware.com/presentations/workshops/redux-fundamentals/intro-history-goals-reducers.html#/15 https://blog.isquaredsoftware.com/presentations/workshops/re... [2] https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-1/#redux-s-influences-and-goals https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-ta... [3] https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation/ https://blog.isquaredsoftware.com/2018/11/react-redux-histor... [4] https://blog.isquaredsoftware.com/2019/03/presentation-state-of-redux/ https://blog.isquaredsoftware.com/2019/03/presentation-state... [5] https://redux-toolkit.js.org https://redux-toolkit.js.org
- jrochkind1 7y agoIs MobX catching on?
- purplerabbit 7y agoNo :( unfortunately it's more of a library than a framework, and there's no broad consensus on how it should be used (although there are certainly some opinions floating about on that front)
- jkleiser 7y agoGreat article! I have only done a few simple projects with React, except for one where we used Redux. For me, Redux is too much clutter. I can recommend a look at this article: https://medium.com/@yiyisun/redux-vs-the-react-context-api-vs-apprun-f324bee8cbbf https://medium.com/@yiyisun/redux-vs-the-react-context-api-v... I've been working on an AppRun based hobby project for a couple of months now, and I find state management there to be quite easy. No clutter!
- steve76 7y agoCan't you just use component composition? You build the children props first, and pass it into the app. function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); }
- pier25 7y ago> You can make API calls in your actions just like you normally would Jesus don't do this. Mobx, Vuex, Redux, etc, are about managing application state not about managing application logic. I started making SPAs in 2015 and I also used actions for everything (API calls, auth, etc). It was fine for small projects but then I got to work on a medium sized project and those actions became huge and controlled everything in my app. I usually consider myself a pragmatic person but that was impractical for a number of reasons and also conceptually wrong. When working in SPAs now I remove anything that is not state management from MobX/Vuex/etc (I don't use Redux anymore). That logic belongs somewhere else. The pattern I'm doing now is that I conceptually separate components from the application logic. The store(s) feed data to the components but if the components want to interact with the application they go through a mediator of sorts (eg: changing a route, modifying state, authenticating, etc). This mediator then interacts and coordinates with the different application modules. For example when authenticating many things have to happen in different modules (API, store, router): 1) An API call has to be made to verify credentials and receive a token 2) This token has be stored somewhere eg: localStorage 3) Maybe we also need to make an API call to get user info, their roles, etc 4) Some state in the store has to be modified. Not only the user info, but the app logic usually needs to know if a user is authenticated. 5) We need to tell the router to go to somewhere eg: /home It makes no sense to do all that in an action. Store actions should be about managing state, period.
- mpfundstein 7y agoWhat about redux-thunk? I usually put all me data fetching logic in thunjs and dispatch to regular actions if needed.
- pier25 7y agoI haven't used it but AFAIK redux-thunk allows you add async middleware to redux. I don't see how adding more features to your store would alleviate the architectural problems I've mentioned. If anything it makes you store even a bigger protagonist in your application.
- nawitus 7y ago
- alfonmga 7y agoI'm in the process of building a B2B multi-tenant SaaS application and one of the best decisions I made so far was to use Easy Peasy [0] to handle the state of my React application logic. It has a very intuitive and powerful API. I would recommend everyone to take it a look as it is not very known. [0] https://github.com/ctrlplusb/easy-peasy https://github.com/ctrlplusb/easy-peasy
- ChrisCinelli 7y agoFor state management I would start with reading "Thinking in React": https://reactjs.org/docs/thinking-in-react.html https://reactjs.org/docs/thinking-in-react.html Step 3 is key. Step 4 is also important. Then try to simplify the app as much as you can. A system has always 2 kind of complexity: the intrinsic complexity and incidental complexity. "Smart engineers" like to show off their intelligence. That usually end up making their life harder by adding a lot of incidental complexity that eventually lead to fragile architecture and frameworks that add more complexity than necessary.
- ChrisCinelli 7y agoWhen a system has high intrinsic complexity good architecture design is one way to mitigate the problem. The best way that I found so far are the principles of Object Oriented Analysis and OO Design ( https://en.m.wikipedia.org/wiki/Object-oriented_analysis_and_design https://en.m.wikipedia.org/wiki/Object-oriented_analysis_and... ) to break down the system in autonomous subsystems that make sense. Generally the best philosophy is keeping things as simple as possible and refactor as new complexity is necessary.
- ChrisCinelli 7y agoI do not like Redux a lot. It leads to a lot of boilerplate code and a lot of extra files. It also exposes transparently the state that lead by default to a coupling between the "internal" data rappresentation and the rest of the UI components making refactoring a nightmare. Mobx at least leads to a more concise codebase. GraphQL and Relay/Apollo/etc help a lot to reduce the business logic in the front and code to written in general.
- acemarke 7y agoAs I've mentioned elsewhere in the thread: - Our new Redux Toolkit package (https://redux-toolkit.js.org https://redux-toolkit.js.org) simplifies most common Redux use cases, - Our new Style Guide docs page recommends patterns that result in simpler code, such as using the "ducks" pattern for single-file logic ( https://redux.js.org/style-guide/style-guide#structure-files-as-feature-folders-or-ducks https://redux.js.org/style-guide/style-guide#structure-files... ) - And we're working on a docs rewrite that will update the tutorials to show simpler approaches as well
- reilly3000 7y agoI haven't seen much mention of Apollo here. It offers a 'full stack' state management solution from server to client to offline cache. Each component can be adopted independently, but they work really nicely together. The tradeoff is that you're writing graphql, but it comes along with a lot of benefits, like consolidating requests etc. On the server-side, graphql can be a thin wrapper over rest apis. Defining good schemas can be a bit of a pain, but they maintain a level of consistency throughout the system as it grows.
- veeralpatel979 7y agoHi - author here. Thanks for mentioning Apollo. I've actually abandoned maintaining any data state in my global stores. I just use Apollo and provide a refetchQueries to my useMutation call to update my UI after mutations. https://www.apollographql.com/docs/react/api/react-hooks/ https://www.apollographql.com/docs/react/api/react-hooks/
- deanclatworthy 7y agoDidn’t catch any mention of hooks in this article. My advice is only use redux when two components need access to the same information within a few seconds of whatever you are doing. Otherwise I’d put all data fetching into hooks which can give any component which uses it access to all kinds of information and the ability to conditionally render. I have found this pattern to be cleaner than using component lifecycle methods. An example can be seen here: https://github.com/deanc/buyxiny.com/blob/master/site/src/components/ItemList.js https://github.com/deanc/buyxiny.com/blob/master/site/src/co...
- hi41 7y agoI am just a regular C/C++ developer. When I read OP's article, I was dismayed by the number of tools or frameworks he refers to. I wish to become a web/app developer. How do I climb such a steep curve? Can someone please suggest a good online course.
- tobyhinloopen 7y agoJust start with a plain React application, follow the react tutorials. Build something simple. Other tools and frameworks are all optional. I’m a frontend dev and I never used many of the tools he mentioned.
- danellis 7y agoWhy is state management such a talked-about issue with React? Other than distributed state, which obviously comes with its own set of challenges, managing state doesn't seem to be an issue with any other language, framework or platform. But with React, it seems to be a major part of the learning curve, with entire tutorials dedicated to it and numerous libraries to help with it in some way.
- acemarke 7y agoBecause React, more so than the other frameworks, puts such a strong emphasis on "the UI output is entirely based on your state", while at the same time there are some limitations to tracking state in ways that are inherently tied to your UI hierarchy.
- danellis 7y ago> the UI output is entirely based on your state How could anything else be true?
- acemarke 7y agoIf you look back at a typical 2011-ish jQuery app, the exact opposite is true. You might have a modal visible in the page, but the only indication code-wise that the modal existed was in the DOM itself, because there was a one-time mutation of the DOM via `$("#my-modal").dialog()`. The only way any part of the app could _know_ if that modal was being shown was to check on the actual DOM itself, like `$("#my-modal").is(":visible")`. Then you start getting into weird bugs because maybe some other script has mutated the portion of the DOM you cared about, or you're having to write logic to handle if the DOM _was_ in this state and now needs to be in _that_ state. What we've learned over the last decade or so is that while that is fine for adding small amounts of interactivity to the page, it's not a maintainable way to build large-scale application codebases. Instead, you want to have an actual `isModalVisible : true` boolean be the real source of truth, and the UI should just be a reflection of that value. I'm not saying that React invented this idea or that it's exclusive to React, just that React's docs and community has generally put a stronger emphasis on that specific mental approach to designing the app: https://reactjs.org/docs/thinking-in-react.html#step-3-identify-the-minimal-but-complete-representation-of-ui-state https://reactjs.org/docs/thinking-in-react.html#step-3-ident...
- sytringy05 7y agoIs it just me or does the state management / prop-drilling thing sound just like a bog standard dependency injection problem?
- rossmohax 7y agoI know nothing about state management on frontend, but please make supporting "history back", "history forward" and "F5 reload" use cases easier. It seems to be a hard problem to crack, given how often these simple actions are broken
- baddox 7y agoIt's really not a hard problem to crack. In most cases where history navigation doesn't work it's due to nothing more than oversight. It'll generally just be someone doing a JS route transition in an onclick instead of just making a JS routing-aware link with an href. There are some cases where history is a bit trickier to get right, like infinite scrolling. Some apps have it working, although with varying levels of success in my opinion. The forum software Discourse does a pretty good job: https://discuss.emberjs.com/t/is-the-six-week-release-cycle-too-frequent/7526/62 https://discuss.emberjs.com/t/is-the-six-week-release-cycle-...
- icedchai 7y agoSPAs have created an "inner platform" effect. Developers are forced to recreate / provide basic browser functionality.
- JoeOfTexas 7y agoShameless plug, but I created a dumbed down version of redux. https://github.com/joetex/flatstore/ https://github.com/joetex/flatstore/ Most of the difficulty with Redux is managing actions and reducers. Out of the box, flatstore lets you get/set from anywhere, with component "connect" function to trigger re-renders on data changes. It's probably only suited for smaller projects, but now you can prototype at the speed of light.
- bowlingx 7y agoI used redux before switching to Apollo and a query parameters / routing based state management. In Web Applications especially, it makes sense to couple global state to URLs. It makes the page and the current state shareable and brings value (like tabs, filters, pagination etc.). I developed a library based on zustand and immer for this case (called Geschichte). It’s open source https://github.com/BowlingX/geschichte https://github.com/BowlingX/geschichte Most of the use cases I can solve with that, the rest is usually either forms (covered by formik, final-form etc), async loading of data (Apollo) or local state and animations.
- PaulDavisThe1st 7y agoSo let me just check that I understand this correctly. A whole bunch of problems that were "solved" (or at least, had fairly good solutions) in the context of desktop GUI toolkits have resurfaced in the context of application development confined to the web browser. Almost none of the previous "solutions" are usable, because of the specifics of the theoretically portable "browser platform" for which all this development is taking place. So instead, an entirely new set of frameworks are slowly being developed to solve the same set of problems that desktop developers worked on 20+ years ago, and are slowly converging on similar approaches? Is that too cynical, or just about right?
- rsynnott 7y agoYeah, no, that seems to be about the size of it.
- heleninboodler 7y agoI'd say it's pretty correct, but I'm not sure I'd agree with what I think your conclusion might be. If you want user interface toolkits based on 20 year old paradigms in your browser, just build your app on something like YUI. :) "Make it like the desktop" was the initial wave of web developers' first instinct, and it wasn't great.
- cactus2093 7y agoGenuinely curious, as someone who wasn't building desktop apps 20 years ago, what are the toolkits you're referring to? When I google "cross-platform desktop frameworks", the entire first page of results are all about the modern toolkits like Electron, Photon, ReactNativeEverywhere, etc. So it seems like your complaint is many years too late at this point. Maybe there were solutions to this 20 years ago, but if I'm starting from scratch today trying to build a cross-platform app, what I'm looking for are frameworks that are mature, well documented with lots of tutorials on how to do basic things, and have a large community around them for getting help on e.g. stackoverflow. It doesn't really matter what has been around longer, if the newcomers have surpassed it on these fronts.
- 7y ago
- andrewstuart 7y agoThis post could more strongly emphasise something important to understand about writing ReactJS applications - taking a declarative approach to the overall application architecture. Trying to write React applications in an imperative manner (the more traditional approach to programming as opposed to declarative), twisted my head in knots and made things harder and more complex than they needed to be. What I mean by writing React applications in a declarative manner is: 1: you have a global state in your application 2: the global state includes an object with information about what state the application is in, for example: globalState = { "DisplayingFormEditUserProfile": false, "DisplayingFormViewUserProfile": true, "DisplayingSideMenu": false, "DisplayingTopBanner": true, "DisplayingUserSignedInStatus": true, etc etc } 3: use React context to manage this state. 4: components decide to display themselves by querying the global state It becomes easy then for your buttons and links to simply adjust the global state to make things happen. Because React rerenders the application each time the global state changes, you need do nothing explicit (imperative) to drive the application - it all just happens automatically as a result of what actions the user takes - actions that alter the global state. React hooks and context make this approach very easy and clean to implement/ That's the way I do it anyway - an approach I find makes the entire application easier to understand.
- snowl 7y agoyou just described redux or mobx (aka the article)
- jaimex2 7y agoBeen using alt.js for ages and continued to make use of it in React, never really ran into the issues described. Use it as a store or cache that alerts all your components of data changes, works well enough.
- veeralpatel979 7y agoHey - author here. This is my first time hearing of alt.js. How does it compare to more popular solutions like Redux and MobX?
- kerkeslager 7y ago> For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color. Wait, what? Hell no. This is literally the core use case for CSS classes. And while there are cases where CSS classes can cause problems, this isn't one of them. This is a case where CSS classes shine. This is like an article on how to use fire to solve problems, and instead of choosing an example like staying warm or cooking food, the author leads with an example like disposing of tires by burning them in the rainforest. It's just an unambiguously bad idea. There were a bunch of places where I wasn't familiar with the tools he's using and am curious, but I'm certainly suspicious of anything he suggests after the "use global state to reinvent CSS classes via JS" suggestion.
- austincheney 7y agoYeah, I have a stylesheet for layout and then one for each color scheme. Changing the scheme is literally changing the class attribute of the body tag. Maintaining state isn’t that much harder.
- jeffadotio 7y agoI have abandoned all of the JavaScript state management libraries and now prefer to simply write my own storage module using Meiosis[1] and a tiny library for operating streams. This replaces tech debt and repetitious boilerplate code with simple, meaningful and functional code. http://meiosis.js.org/ http://meiosis.js.org/
- wruza 7y agoMay I ask React guys what’s wrong with parallel controller tree and (virtual) view tree which React tries to avoid (and, in my opinion, creates a bunch of in-club issues on a way that didn’t exist outside of its paradigm)? I’m talking about this desktop mvc adapted to hyperscript approach: class AppCR extends CR { ctor() { this.db = new RemoteDB this.sidebar = new SidebarCR this.content = new ContentCR(db) this.val = {a:42, b:3.14, c:this.db.fetchC()} } render() { let {h, val} = this return h.div.wrapper(null, [ this.sidebar.render(), this.content.render(), h(AuxViewType1), h(AuxViewType2.model(this.val, "c")), h.div(val.b), ]) } } class AuxViewType1 extends View { render() { let {h, cr} = this return h.span(cr.val.a) } } class AuxViewType2 extends View { render() { let {h, model} = this return h.model_input({model}) // model == {object, key} (for "input") } } Your controllers localize data, speak to the model in a non-ambigous way, provide data for views directly (no props, think view.cr and view.model works at any depth) and integrate other controllers by creating/destroying them explicitly and rendering when they are needed. You also may have a global read-only (mostly) store of reference data, but that’s more for caching large datasets, not for state sharing. Btw, model_input could wait on a val.c promise and rerender appropriately without your intervention (something React folks were to deliver last year, supposedly with help from async setstate process, which was a requirement for that (why?)). All of this React thing honestly feels like Haskell sneaked into an enterprise for the sake of “so cool we did it” giggles. Dan’s comments on this did not help much when I read ‘em.
- deleted 7y ago[deleted]
- rwieruch 7y agoWith the release of React Hooks and the advent of GraphQL + its client-side libraries, state management [0] became a blessing IMO. Whenever it's possible I would opt-in using GraphQL for client-server communication. Then popular libraries like Apollo Client help you with all the state management for remote data and its caching. After this, all the remaining state management can be done with React Hooks (useState, useReducer, useContext)[1]. Only if this gets out of hands, opt-in Redux/MobX/... - [0] https://www.robinwieruch.de/react-state https://www.robinwieruch.de/react-state - [1] https://www.robinwieruch.de/react-state-usereducer-usestate-usecontext https://www.robinwieruch.de/react-state-usereducer-usestate-...
- nojvek 7y agoThis post is overly complex in its suggestion of libraries. He does have a good point of understanding different kinds of state and that state management in front end is perhaps the hardest problem. If you think about it well earlier, a lot of things become easier as you scale.
- veeralpatel979 7y agoHey - author here. I agree that libraries for certain things, like MobX/Redux or Formik, may be overkill. What's more important, though, is that people choose an appropriate equivalent to these libraries. Replace the library, but not the method of organizing state. For example, feel free to replace MobX/Redux in my post with your own global state management solution. But I recommend replacing it with a global state management solution, not something else.