8 ms·
Idiomatic Redux: Implementation and Intent
- toprerules 9y agoWhen I first came to React my understanding was that React/Flux were all but a package deal, and that I better use Redux because it seemed so popular. Redux quickly became a pain point in terms of boilerplate and added cognitive load. Now a top level stateful component works for 90% of my use cases.I feel the same way about React Router. Maybe if you're building something quite large or complex these prepackaged tools make sense, but I personally have gone the way of Golang and started to prefer a little copying over adding an extra dependency and I've found that React is wonderful by itself. I encourage anyone who will listen to try ditching Redux in favor of a single top level stateful component.
- learc83 9y agoI wouldn't recommend this at all. Sure if you have a tiny site with very limited functionality or you're learning react, you don't need redux. But if you're using components they way they're intended, (not cramming everything into a few overloaded components), you're quickly going to be passing down props down through layers and layers. It will quickly become a maintenance nightmare. Just imagine that you have a button component that is 5 or layers down from you're stateful component. It's getting props to control it's state passed down through all 5 levels, and it's getting functions to change the stateful component's state passed down through all 5 levels. Now what happens when you want to move the button from your side bar to your footer? Redux solves a very real problem, there are other solutions to this same problem, but eventually any non-trivial app is going to need something for state management beyond a single top level stateful component.
- zepolen 9y agoHow does using Redux solve the tons of props through layers problem?
- acemarke 9y agoBecause you can use the React-Redux `connect()` function to wrap any component in your tree with a "container component" that automatically subscribes to the Redux store, extracts the data you want for that component, and passes it straight in. That way, the upper components in your application don't need to know that a leaf component happens to need a specific value and pass it down through N levels of components - that one component just grabs the data it needs from the store. A good example of this would be a list or a treeview, where every list item or treeview node is itself connected to the store. I talked about some of the benefits of using Redux in a React application in a post I co-wrote: https://www.fullstackreact.com/articles/redux-with-mark-erikson/ https://www.fullstackreact.com/articles/redux-with-mark-erik...
- zepolen 9y agoThat sounds completely opposite to the React functional philosophy and is akin to using global variables in a regular program. Have fun testing that!
- egeozcan 9y agoYou can test the base component or test the connected component with a dummy state. If your state is shallow, this is very easy to do.
- Jare 9y agoMy second problem with Redux, besides the common one about boilerplate, is precisely the mismatch between the recommended, RDBMS-like shallow store, and the typically tree-like OO nature of both the UI and the application logic. I understand that concrete containers act as adapters between the store and a given level of the application/UI tree, but this "impedance mismatch" is a real drag.
- mateuszf 9y agoIt's actually trivial to test, because all of the state is known at any time. So yes - kinda global state, but exact full state as input and exact full state out as output. Which you can test sliced into parts with as many tests as you want.
- ilovecaching 9y agoI actually find it easier to reason about my components when I can trace data sharing in my hierarchy rather than allowing components to cheat and circumvent sharing state in their common ancestor. I think it's idiomatic React to construct hierarchies of functional components topped by very few stateful components. Using connect() to turns components deep in the hierarchy into stateful components can only lead to a less declarative dataflow that tightly couples those components to Redux for very little gain. If you're having difficulty moving components around because of state consider that you may be thinking too statefully and should try to find a less stateful way to describe your UI, or that the UI you have constructed does not adequately group related pieces of data and functionality together, which will lead to a confusing UX.
- qudat 9y agoI get what you're saying but for large react applications using Redux helps to dramatically reduce state complexity and increase maintainability. Having one function 5 levels deep that receives its props from a top level function means everytime you add a prop you have to modify 5 function signatures to get that new data. That quickly becomes unsustainable when you have 10+ layers. What happens when you want to modify the prop that is set in the parent? You have to pass through a callback function that will modify that parent props and then trigger a re-render. Very quickly you end up with problems that flux/redux/mobx try to solve.
- learc83 9y agoThere is no way you're maintaining anything more than trivially complex apps like this. It was never built to be used that way. Passing down chains of attributes and functions through 5+ layers isn't idiomatic React because React was created with Flux in mind--this is not the intended methodology. >If you're having difficulty moving components around because of state consider that you may be thinking too statefully and should try to find a less stateful way to describe your UI That is a meaningless platitude. State exists, you can't remove it. No matter how much you minimize it, you're going to eventually end up passing props down through an arbitrarily large number of layers if you have no state management system in place. Language designers realized this was a problem decades ago--it's why we have scoping rules. Passing down chains of arguments through layers and layers of functional calls is unwieldy. And it's worse when each one of these functions is directly producing user output.
- acemarke 9y agoHey, that's my post! :) Actually submitted this to HN previously, but didn't get any traction. Ah, the fickleness of upvotes :) Spent a lot of time researching for this post. What I really _wanted_ to write was the second half of Part 2 [0], where I gave my opinions on why certain usages are good ideas or bad ideas, but I realized I needed to cover a lot of background before I got there :) Was really cool reading through the original discussions and seeing the vision Dan and Andrew had for Redux from the beginning. Hopefully this pair of posts helps clarify a lot of the discussion around Redux and its common usage patterns. Also, I'm always happy to answer questions about Redux (and React) usage, and encourage people to come by the Reactiflux chat channels on Discord [1]. Always a great place to learn and ask questions about React, Redux, and related technologies. Finally, if anyone is interested in learning React or Redux, I keep a big list of links to high-quality tutorials and articles on React, Redux, and related topics, at [2]. Specifically intended to be a great starting point for anyone trying to learn the ecosystem, as well as a solid source of good info on more advanced topics. [0] http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-part-2/ http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao... [1] https://www.reactiflux.com https://www.reactiflux.com [2] https://github.com/markerikson/react-redux-links https://github.com/markerikson/react-redux-links
- deleted 9y ago[deleted]
- rwieruch 9y agoCongrats Mark! :)
- forgottenacc57 9y agoThis article serves only to reinforce that the beautiful simplicity of react is spoilt by the confusion of Redux. I feel that every Redux blog post should start by pointing out that despite react/Redux almost being discusssed synonymously, as the author of Redux points out, "you might not need Redux". I would go further and say "avoid Redux until you know you need it", and point people to more simple ways of reaching the same goals. Redux is a power tool for experts that is disheartening beginners and sending them down the wrong path. I was speaking to someone recently who had tried Reactjs. I wanted to enthuse with him about how awesome it is. He said it was too hard and he went with vuejs instead. When I questioned him further it became apparent that he had started to learn react/Redux and gave up in confusion.
- ChicagoBoy11 9y ago>Redux is a power tool for experts that is disheartening beginners and sending them down the wrong path. I've seen this expressed many, many times, and it is something that I've honestly never quite understood. I started with React less than a year ago, and it wasn't until I added Redux to my stack that I began to feel perfectly comfortable with, To me, the fact that actions dispatched in whatever component would cause the global state to be recomputed which then got handed to react to render it just a no-brainer. This truly makes data in React flow just one direction. The tooling around it -- Redux DevTools -- also made seeing my app in action and debugging it so incredibly clear. Without it, for me initially it was a struggle about where exactly to store certain state, and passing props and callbacks up and down all over the place. It wasn't clean or easy to reason about at all. As someone who had very limited experience in the framework, Redux felt like a tool which made the pit of success for me pretty darn large. I do know that even Dan himself has suggested that people not learn them in conjunction, but to me, the coupling of them is so perfect that React only became a joy to use WHEN I introduced it.
- djfm 9y agoMy thoughts exactly! The amount of things you need to know in order to use react and redux is ridiculously low. You're basically just composing functions of two types "(state, action) => state" , and "state" => html. The boilerplate consists of a few functions (really mostly createStore and connect) that are incredibly well documented. There's almost nothing to understand and redux itself is almost not a lib (only 318 LOC). The whole thing is a pattern and redux is just a small, very well written key component of the pattern. When people debate redux they debate about programming, not about a tool. That's why it's interesting and why it will never end.
- acemarke 9y agoWhenever a Redux thread pops up, there's inevitably complaints about "boilerplate". I'd like to pre-empt those complaints a bit by pointing out that, per part 2 of my post, it's entirely up to you how much abstraction you use in your own Redux app. If you want use Redux for a particular use case, someone has probably already written an addon or utility to help solve that problem, and I've got them listed in my Redux addons catalog [0]. If you don't want to edit multiple files, use the "ducks" pattern to put actions and reducers in a single file [1]. If you don't like writing action constants by hand, use one of the dozens of action/reducer generation utilities out there [2] [3]. If you don't want to manage normalized data by hand, there's many addon libraries to help with that [4]. I specifically show off one of them, Redux-ORM [5], in my "Practical Redux" tutorial series [6]. I love all the tools and addons that people are building on top of Redux. One of my favorite quotes is from a Redux issue thread: > Redux is a generic framework that provides a balance of just enough structure and just enough flexibility. As such, it provides a platform for developers to build customized state management for their use-cases, while being able to reuse things like the graphical debugger or middleware. A few months ago I opened up a Redux issue thread to discuss further ways that we could help make it easier for people to use Redux, and solve complaints about "boilerplate". The discussion kind of trailed off, but I would love to have more discussion and ideas for building useful new tools on top of and around Redux to help people solve problems and build useful applications. Please join the discussion here: https://github.com/reactjs/redux/issues/2295 https://github.com/reactjs/redux/issues/2295 [0] https://github.com/markerikson/redux-ecosystem-links https://github.com/markerikson/redux-ecosystem-links [1] https://github.com/erikras/ducks-modular-redux https://github.com/erikras/ducks-modular-redux [2] https://github.com/markerikson/redux-ecosystem-links/blob/master/action-reducer-generators.md https://github.com/markerikson/redux-ecosystem-links/blob/ma... [3] https://github.com/markerikson/redux-ecosystem-links/blob/master/reducers.md https://github.com/markerikson/redux-ecosystem-links/blob/ma... [4] https://github.com/markerikson/redux-ecosystem-links/blob/master/entity-collection-management.md https://github.com/markerikson/redux-ecosystem-links/blob/ma... [5] https://github.com/tommikaikkonen/redux-orm https://github.com/tommikaikkonen/redux-orm [6] http://blog.isquaredsoftware.com/series/practical-redux http://blog.isquaredsoftware.com/series/practical-redux
- forgottenacc57 9y ago
- sAbakumoff 9y agoI don't event want to open the article that has both of "idiomatic" and "tao" words in the title.
- acemarke 9y agoFor what it's worth, "Practical Redux" is my blog series that demonstrates Redux techniques by building a sample app. "Idiomatic Redux" is my series for my own thoughts on why I think certain Redux usage patterns are good or bad. The phrase "Tao of Redux" was just something that popped into my head and sounded catchy, and seemed to go along with the idea of "explaining the Redux philosophy".
- sAbakumoff 9y agoIt sounds like the author is some kind of guru who became enlightened and willing to share the "tao" of "idiomatic" way of JS library. Can't you see how pretentious it it?
- acemarke 9y agoWell, _I_ am the author, _and_ a Redux maintainer, _and_ I've spent a ton of time discussing and using Redux :) I can legitimately say I'm _an_ expert on Redux, its implementation, and its use. The original point of writing it is that I see many people complaining about things like "having to use action creators" and "having to edit many files", when Redux itself doesn't actually require you to do those things. So yes, I guess you could say that I _am_ trying to enlighten people, because many of the complaints about Redux are being made out of an incomplete understanding of why it exists and what its intended use is. If people still don't like Redux after that, that's fine - I just want people to better understand why Redux usage patterns exist and clear up the misunderstandings.
- sAbakumoff 9y agoIMHO experts who want to help people differ from self-proclaimed gurus who want to enlighten people. David Robinson ( data scientist not a basketball player) is the example of the first type. Take a look at his blog. He does not make any pretentious title or something, yet his articles are truly enlightening and influence millions of people!
- davedx 9y agoI've just started another side project using React with redux. One of my goals is to keep my code as simple as possible while still being able to build a large application. To keep it simple, I'm not using react-redux (I often try to avoid this), and I generally pass the entire store down through all child components, again to keep it simple. My top level app with its simple navigation code subscribes to the redux store and sets its state from it. My router is just a bunch of statements like {pageName === 'connnections' && <ConnectionsPage {...props}}. To avoid too much boilerplate, I find most of the time all you need are some helper functions. I have a helper function called 'action' that I call like action('LOAD_SOME_DATA') that dispatches on the store with type: 'LOAD_SOME_DATA'. I use a simple 'getIn' function to access nested data, and I mutate data in my reducers with object-path-immutable. Everything is clean and easy to read. I believe one of the biggest problems with the React community is the constant bleed of 'performance' into app design and development. Premature optimization is everywhere. Guess what, most apps won't need immutable data and free componentShouldUpdate checks, memoized selectors, or subscribing to subsections of the store using complicated 'connect'/high order component plumbing. The one time I actually had a perf issue with React was when I had a GIGANTIC list of data (showing all the active salespeople in our company -- we have a lot), which due to me storing form state in redux was being completely re-rendered every keystroke. The solution wasn't to add react-redux or start using immutable data - it was to add paging and filtering. I improved performance, but more importantly, the UX of the app with this. If your React app really is so huge and complex that it needs reselect, react-redux, react-router, great stuff -- you are obviously building software for a successful product with lots of feature requirements. Most apps I've worked on never got to this level of complexity.
- Nimelrian 9y agoPassing the store (or in general, your application state) down from each component to their children has one big flaw: It reduces your components' reusability in other projects. You can circumvent this by creating a component at the top level of your application which holds the state and passes it down via React's context. The state-dependent components can then pull the state from context where they need it. By doing so, you basically reinvented react-redux, which works just like this (+ the performance optimizations). Regarding your issue with the complete app rerendering on a single keystroke: This is a result of you passing down the entire store to each component. Since you still edit the store in an immutable way, on every keystroke you create a new store object and React rerenders everything because the store prop changed. I'm also confused how using paging/filtering would help with this. Another advice would be to not store form state in redux. Keep your redux store only for your actual model application state. Keep forms/temporary state in your components and move it to your model redux store once it is done/baked.
- softwarelimits 9y agoHihi, I catch myself scanning through react posts just to find something about vuejs :) - it seems to be kind of a 'natural social law' that 'the mainstream' never is the real thing.
- mg74 9y agoJust want to put down my flag in the sand and say I love Redux. It is my tool of choice whenever dealing with anything that contains state and (with it's partner in crime, Redux Saga) anything that has to do with the real world (dreaded side-effects). Thumbs up on Redux, your boilerplates are my sanctuaries.
- nikolay 9y agoThe React ecosystem is growing out of control! JavaScript was a revolt against the overcomplicated Java ecosystem, but it is now worse than its original enemy with favors, opinions, and best tools for the job changing daily! Remember when JSON came out as an alternative to XML? Now all XML technologies have JSON equivalents - schema, pointer, etc. What was the point of the switch then - keep spinning wheels and not moving forward? With Babel being the centerpiece of JavaScript development today, can we at least apologize to GWT?
- qudat 9y agoI use react/redux for a very large application and overall it has been a powerful, robust, and scalable solution that handles 99% of our use-cases. The biggest problem I struggle with is knowing when components rerender. Libraries like reselect that memoize state-composed functions really help with performance, but make it less clear when things are being rerendered and why they are being rerendered. I also agree with some of the posts here that handling async actions, or side effects, can be rather difficult in the redux model. Redux-thunk is still being recommended even though Dan created it more as a bandaid solution rather than something more robust. We solved it with redux-saga, but my colleagues -- experienced developers -- struggled initially to grasp generators. I also think sagas run into the same problem that one action triggering multiple reducers has which is you could inadvertently trigger an action that hits one or more sagas and not realize you are causing side effects that you did not intent. I know there have been countless discussions on what to do about this problem, but I fear the solution will always be outside of the scope of the core library because of backwards-compat.
- acemarke 9y agoI have links to a number of useful utilities for visualizing when and why components re-rendered in the "DevTools#Component Update Monitoring" section of my Redux addons catalog [0]. Hopefully those help. I'm a bit curious why you say that "reselect makes it less clear when things are being re-rendered". The overall reasons shouldn't change - connected components will re-render when values from `mapState` or props from the parent component have changed by reference. Thunks are definitely the "minimum viable approach" for handling async logic in Redux, or at least the minimum _suggested_ approach (as opposed to doing async handling purely inside of a component). I agree that sagas (and observables) are a pretty big jump for most devs to make, but then again, async logic in general is more difficult to reason about. Personally, I still think most devs should use thunks until they see a need for something else (per my post "Idiomatic Redux: Thoughts on Thunks" [1]). Redux does deliberately push the question of "how do I handle async logic?" outside of its core, and leave it up to you. That's both because Redux focuses on structuring synchronous state updates, and because (per my post) Dan and Andrew wanted to give devs flexibility in what specific technique they use for handling async. [0] https://github.com/markerikson/redux-ecosystem-links/blob/master/devtools.md#component-update-monitoring https://github.com/markerikson/redux-ecosystem-links/blob/ma... [1] http://blog.isquaredsoftware.com/2017/01/idiomatic-redux-thoughts-on-thunks-sagas-abstraction-and-reusability/ http://blog.isquaredsoftware.com/2017/01/idiomatic-redux-tho...
- Jare 9y agoThe article does a great job of presenting Redux and stripping the "magic" out of it. But the section about dependencies between reducers bugs me A LOT. "If a CommentsStore needed data from a PostsStore to properly update itself, it could call PostsStore.waitFor() to ensure that it would run after the PostsStore updated. [...] with Redux, that sequencing can simply be accomplished by explicitly calling specific reducer functions in sequence." And it introduces a specific call order of slice reducers, and 'hasCommentReallyBeenAdded' variable, inside the root reducer to implement that dependency. But it looks to me like such a dependency will usually be specific to a certain action, and in a modestly complex application, different actions will have dependencies in different orders, with different ordering and information needs, and moving those into the root reducer doesn't scale beyond one (i.e. the shown example). Thus, as is, the section doesn't really provide a useful pattern for building real apps. Those will need a different approach - represented by the catch-all but not so useful "it's all about how you want to write it." Am I missing something about that section and topic?
- acemarke 9y agoMissing a bit, yeah. It's entirely possible that the `commentsReducer` does know how to handle all relevant actions. It's also possible that it only needs that extra data for one specific action. One of the biggest advantages of the "reducer" concept is that _they're just functions_, and you can mix, match, and combine them in any way that works for you. I show several examples of additional custom reducer structures in the "Structuring Reducers - beyond `combineReducers`" [0] section of the Redux docs, and also in my blog post "Practical Redux, Part 7: Feature Reducers" [1]. As a quick summary, instead of having the `combineReducers`-generated function as your root reducer, you can further wrap that up in other functions, such as one that takes an array of reducers and runs them in sequence, or one that only does that special handling for a specific action case and otherwise delegates all handling to the normal `combineReducers` approach. [0] http://redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html http://redux.js.org/docs/recipes/reducers/BeyondCombineReduc... [1] http://blog.isquaredsoftware.com/2017/01/practical-redux-part-7-forms-editing-reducers/ http://blog.isquaredsoftware.com/2017/01/practical-redux-par...
- saosebastiao 9y agoI'm infinitely more comfortable with functional composition over object composition, but Mobx is still far more intuitive to me. It's because functional composition over a constantly changing global state tree is a huge fucking mess. There's a reasonably objective explanation as to why it is a mess, and more specifically, why Redux suffers from so much boilerplate and painful refactorings. In UI programming you are managing state, full stop. Almost any UI can be described as a state machine. Not just GUIs, but any UI: digital, analog, mechanical, whatever. And since you are basically managing state machines, any refactoring of the functionality of your UI is fundamentally a refactoring of your state. Rendering is a functional refactoring (state doesn't change), but if you are building a UI and not just a simple landing page, you are far more likely to refactor functionality over rendering...meaning your data is more likely to change than your functions. In other words, UI programming via functional composition over a frequently changing global state tree falls on the wrong side of the Expression Problem [0]. Every time you change your data, you have to modify every functional pipeline, from beginning to end, that touches that changed section of your state tree. That is where all the boilerplate comes from, and that is why Redux is consistently criticized as a system that becomes more and more unwieldy as your UI complexity grows. It is a bad abstraction for the problem at hand. Mobx is a far better pattern IMO, and I feel entirely comfortable saying that is an objective position to take as long as your functionality is more likely to change than your rendering. It isn't that Mobx is a superior idea, it's just the fact that it embraced object composition for a use case that is perfect for it. [0] http://wiki.c2.com/?ExpressionProblem http://wiki.c2.com/?ExpressionProblem