I recently inherited a moderately sized Redux app with over 1300 source files! It's not a trivial app but it's not a word processor or videogame, either...
I recently inherited a moderately sized Redux app with over 1300 source files! It's not a trivial app but it's not a word processor or videogame, either...
I'll also post a link to the slides and livestream video for my React Boston talk this last weekend, where I talked about the Redux ecosystem and gave an overview of useful Redux libs that can help with various use cases: http://blog.isquaredsoftware.com/2017/09/presentation-might-... .
It shouldn't be. If you don't like a tool, don't use it. Nobody is forcing you to use redux, you don't need it, even if you're building a complex react app, setState works just fine, seriously.
> The cognitive overhead of building and maintaining your own framework piecemeal can be detrimental to shipping code
So don't do that. Identify the problem, then use the tools that solve the problem, if you can't figure out which tool is the right one for the job that's your problem, not the fault of the tool. If I need to drill a hole in my wall I don't complain about how many drills are on the market, I just use the simplest tool to get the job done or hire someone who knows what they're doing
> especially if you don't have pragmatic and decisive leadership on your team.
The tool is not responsible for someone failing to use it correctly, that's absurd.
This is hand-waving to deflect Redux from legit criticisms, and is part of the reason I find both responses frustrating and unnecessarily dismissive.
You need to build your own framework for React SPAs because they're intentionally un-opinionated about the pieces you use. Just look at the scaffolding in place for create react app and all the various libraries. I would much rather solve real problems than discuss how to get Immutable to play nicely with Typescript or argue thinks vs. middleware.
"Get good" is a poor response to anyone wanting to see the wildly used/discussed state management solution for React get better, and implies that there aren't good devs out there with experience outside of JavaScript saying there is a better way.
You're right though - there are choices. I didn't like how the JavaScript ecosystem was progressing so I made a choice to not deal with it anymore professionally and it's been a very rewarding career change.
The comment I responded to is complaining that a list of tools makes them feel exhausted, that's not "legit criticism" that's low-effort whining. The legit criticism already received a response in the form of a list of tools meant to obviate the specific issues outlined by the critic, but somehow those tools being available is cynically characterized as a negative thing because some people don't know when or how to use those tools. That's their fault, not the tool's.
> You need to build your own framework for React SPAs because they're intentionally un-opinionated about the pieces you use
This is an engineering trade-off, it's not inherently wrong or bad; it's like whining that you have to spend time learning how and when to use a clutch when operating a manual transmission vehicle when an automatic would just figure it all out for you. Sorry, just because an automatic has a lower learning curve doesn't mean its a superior car, it's just a tradeoff, convenience over control.
> I would much rather solve real problems than discuss how to get Immutable to play nicely with Typescript or argue thinks vs. middleware.
So. Don't. Do. That.
You don't need those things to use React, that's a choice you're making because you apparently desire the features that those tools afford you as a developer, yet you're complaining because you don't like the consequences of the engineering decisions YOU made. I've written and deployed around eight React projects into production and while some of them use immutable.js, none of them use typescript because the team's experience with typescript is limited. You see that? We don't know how to use typescript... so we don't... and everything is fine.
> "Get good" is a poor response to anyone wanting to see the wildly used/discussed state management solution for React get better,
My response is not "get good", my response is quit complaining that tools exist that you don't like. You don't have to use them, especially in the React world where you get to pick and choose the tools that work best for you; if you find the choices overwhelming that's your fault, it's not the tools fault that it exists.
> and implies that there aren't good devs out there with experience outside of JavaScript saying there is a better way.
This is hilariously arrogant. People who don't work with JavaScript want to complain that there is a better way to do work in JavaScript. If there is a better way then put your keyboard where your mouth is and build something better rather than complaining about the existence of tools you don't like.
- I don't already understand how an app using redux works when it reaches a decent size; therefore don't know what problems it will have and need to learn more
- I can't start using redux to understand those problems without making important architectural choices early on, i.e. between sagas/thunks, ducks/not-ducks, or how to remove duplication from reducers, because the community has so many parallel solutions to any given problem; therefore, I need to write a toy app instead of using it on a real product
- redux on its own seems to need about ten packages before you can load information from a json api and pass it to react, a task which is the basis for most toy apps; therefore, every toy app I write seems bloated and heavyweight
It's a hard ecosystem to bootstrap yourself into.
I'm not sure why you say that "you need 10 packages to load JSON data". Assuming that you're in a browser that supports the Fetch API, you need _no_ additional packages at all. You can make an AJAX call in a connected component, and call `this.props.dispatch({type : "DATA_LOADED", payload : response.data})` in the success handler.
Now, we do encourage people to try moving that logic out of components to make it more reusable, which usually means using thunks. However, redux-thunk is all of 12-ish lines long, and if you'd prefer not to actually add it as a separate package, it can easily be pasted into your app directly. But, you genuinely shouldn't _need_ more packages than that to be making AJAX calls. There's plenty of additional abstractions that are available around making network requests and processing the results, but those are all purely optional.
Besides the other "discussion about abstraction" issue I just linked in another comment, I also recently opened up issues to discuss general docs improvements [0] and a rework of the "Ecosystem" page [1]. I really would like to have some docs sections that give more opinionated advice for topics such as "what side effects lib do I use?". Unfortunately, my own free time is already very limited, but I would _love_ to work with people from the community to add new docs sections and update the existing ones.
I also agree that there's a bit of a gap between the "TodoMVC" level and the "building a full-blown production app in the real world" level, in terms of docs and advice. I'd love to see some new docs sections that help with that aspect as well.
So, genuine request to you, and any other readers/Redux users out there: if you are interested in helping improve the Redux docs, PLEASE let me know! Ping me on Twitter or Reactiflux, file an issue, or leave a comment on an existing issue, and we can figure out how you can help us make the docs better and make it easier for people to level up along their Redux journey.
This sounds like a problem of inexperience (which is a problem everyone faces when using a new tool). If you've never used Rails, you're not going to understand the long term implications of maintaining and scaling a complex Rails application. If you want to know what problems you will encounter, you'll have to read a book or learn the hard way, the same as any other tool.
> I can't start using redux to understand those problems without making important architectural choices early on,
This means you're not experienced enough with redux to be making those choices and that's ok; use an alternative tool that you understand well, hire someone with the relevant experience, read a book, or understand that you'll have to do some refactoring later on if you make the wrong choices today.
> redux on its own seems to need about ten packages before you can load information from a json api and pass it to react, a task which is the basis for most toy apps; therefore, every toy app I write seems bloated and heavyweight
This is not correct, but also "not even wrong" in the sense that this concern is outside the scope of redux. If you want to use a bunch of redux related data packages that's fine, you're also free to use GraphQL, Backbone, fetch, jquery, webosckets or straight up XMLHttpRequest. When I hear "I need 100 packages just to do X" it's usually a sign to me that the developer is somewhat inexperienced with the ecosystem and tends to defer to a package whenever they need to solve a problem; this is usually the wrong approach, it's like someone complaining "I need 10 packages just to do string padding"... well no, you don't, just write that code yourself.
> It's a hard ecosystem to bootstrap yourself into.
So is cross-platform game programming and there are hundreds of different engines, toolkits, SDKs, editors and other utilities spread across a dozen different languages. Which one should you choose? I don't know it's all too confusing, they should just stop making new tools so I can catch up.
This is the fundamental problem. In order to learn Redux, you need experience with it. In order to get experience, you need to work with Redux. In order to work with Redux, you need to learn it.
You use the example of Rails, but Rails is the poster child of opinionated frameworks. For someone trying to learn it, there's no ambiguity about how a project is structured. The same is true of Unity or the UDK: there's a single tech choice to make, after which you can rely on the framework to guide you.
I understand that the js community prefers small composable libraries, and agree with the reasoning, but with Redux this is taken to extremes: a tiny core library, documentation that's mostly boilerplate, and much greater cognitive overhead than any other state management.
Also, as unofficial keeper of the Redux docs, I'd also appreciate any suggestions for improving them. Can you clarify what you mean by "documentation that's mostly boilerplate?
If you're looking for more of an "integrated" Redux experience, you might want to look at https://kea.js.org/ .
By boilerplate I mean that the docs are written using examples of e.g. a single network request and a handful of actions, where even a small application with a few repeated bits of code would introduce abstractions for common elements. Because the docs lay out the simplest possible way to do this, they don't fully represent a real app, and because the ecosystem is so vast, it's not obvious which abstractions are state-of-the-art.
Filling in the "next steps" tab with some hints about where to go would probably help a lot.
If you or anyone else reading would like to contribute writing that page, please leave a comment and let me know!
And yes, it's always tough to balance the various audiences and use cases when writing docs. Minimal examples like a TodoMVC can be helpful for showing off basic concepts and keeping a learner from getting too lost, but then those don't cover the complexities of a real-world app.
So strken, acemarke and others: Feel free to reply here, contact me on reactiflux (@mariusandra) or shoot me an e-mail if you feel like talking about Kea.
Cheers and thanks for all the nice words so far. :)
And you're welcome! I probably spend a bit too much more time answering questions than I really should, but hopefully it's helpful :)
I ended up making a dead-simple merge(oldObj, newObj) function that's halfway between Object.assign({}, ..) and lodash.merge() (it doesn't merge arrays)[0]. It makes it easy for me to update deeply nested state trees, provided the nested data is simple enough.
As a result, 99% of my dispatches uses one action type, PLAIN_MERGE, and a dumb state tree indicating what I'm updating, like so:
const handleChange = (values) => {
dispatch({
type: PLAIN_MERGE,
state: {
scatterPlots: {
plotSettings: {
[selectedPlot]: {
[axis]:
clipValues: {
lowerBound: values[0],
upperBound: values[1],
},
},
},
},
},
},
});
};
That might be some deep nesting, but it actually reflects the structure of the interface too, so it's kinda self-documenting.The associated reducer is simply return merge(state, action.state). Again, maybe I'm missing something but it seems like a pretty simple and effective solution to me?
[0] https://gist.github.com/JobLeonard/c4292594ce4d439ab20ec109f...
There's actually prior art for what you're doing (per the articles at [0] and [1]). The code will work, and the changes should show up in the Redux DevTools. Dan even described that as a possibility in some of the earlier Redux issues ([2], [3]).
Now, having said all that: I _personally_ would say that a "single SET_STATE action" approach goes against the intended spirit of Redux. One of the core principles behind Redux is that it should make it very easy for you to understand when, why, and how your state was updated, and what part of the application triggered that state update. If you only have a single SET_STATE action type, then reading the action history log in the DevTools won't tell you much useful. You can see the action contents and the diffs, but the action log itself won't have any semantic meaning, and you can't easily trace back to where the action was dispatched because the entire codebase is dispatching the same action type.
Redux also doesn't care if you do "setter-style" action naming like SET_USER_NAME and SET_USER_ADDRESS, or "event sourcing"-type naming like "USER_ATTRIBUTES_UPDATED". However, either of those approaches is going to at least provide _some_ semantic meaning, making the action log easier to read and the origin of the action easier to trace.
I'd encourage you to read through the "Structuring Reducers" section in the Redux docs [4]. In particular, the "Normalizing State Shape" page [5] talks about why we recommend normalizing your data into a flatter structure, rather than keeping it nested.
Finally, you might want to read through my two-part blog post "The Tao of Redux" [6], which goes into detail on the history and design of Redux, the intent for how it _should_ be used, and why common usage patterns exist.
[0] https://medium.com/@jeswin/implementing-redux-is-tedious-but...
[1] https://medium.com/@benevolentNinja/minimal-redux-setup-e6a1...
[2] https://github.com/reactjs/redux/issues/155#issuecomment-113...
[3] https://github.com/reactjs/redux/pull/140#issuecomment-11395...
[4] http://redux.js.org/docs/recipes/StructuringReducers.html
[5] http://redux.js.org/docs/recipes/reducers/NormalizingStateSh...
[6] http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao... , http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao...
> when, why, and how your state was updated, and what part of the application triggered that state update.
Well, when I said "single" action I was fudging a bit; I have a switch statement with a dozen action types that all are equivalent to the PLAIN_MERGE action. The point is more that I don't have much code inside my actions and reducers, except for the stuff that needs to see the "whole program state" - everything else tends to be encapsulated inside the components.
Also, I think you overstate the lack of semantic meaning: that state tree I pass along, together with the occasional custom action type tag, tells me exactly where the dispatch originates from. Of all the problems I have when debugging my app, finding the source of the dispatch is never one of them.
Tell that to the previous developer. To be blunt, if your library isn't very usable without helper utils or wrappers, it probably isn't really doing its job.
This is my first time using Redux, and my experience has been quite negative. This project has so many reams and reams of code that just doesn't clearly _do_ anything. Line after line of throat clearing and pseduostructure.
I accept that this may just be a poorly written application. I've looked over the Express code and that's not much better, which is remarkable given that all it does is proxy API requests. But all these action creators and reducers and constants seem to have added a lot of indirection to what is still thoughtless and illiterate code.
We are seriously talking about rewriting the whole thing. For a one year old project that's quite damning. Can you point me to an open source codebase that might convince me Redux-done-right makes for readable code?
There's a commonly repeated phrase that "Redux is a pattern, not a framework". Redux doesn't have anything built in for actually updating the state, or defining an application architecture. Redux primarily provides a pattern for separating the write logic from the rest of the app conceptually, and adding centralized behavior on top of that via middleware and store enhancers. So, how you build and organize your application around that pattern is up to you.
Similarly, Redux does not _require_ that you use action creators, action type constants, or that you separate everything into multiple files by code type. Those are common _conventions_. It's absolutely legal for a Redux-connected React component to do:
this.props.dispatch({type : "ADD_TODO", text : "Buy milk"})
However, there _are_ reasons why those patterns exist in the first place.So, a few thoughts. First, I'd encourage you to take the time to read through my two-part blog post "The Tao of Redux" [0], which goes into detail on the history and design of Redux, the intent for how it _should_ be used, and why common usage patterns exist.
Second, while I don't have a specific Redux codebase to point to as a "shining example of best practices" strictly off the top of my head, I do have a list of some selected Redux applications that may be worth looking at. Some of them are purpose-built examples, and others are "real" apps. See the list at [1] - I know that there's some good codebases in that list. I'll also suggest taking a look at "Project Mini-Mek", the sample application for my own blog tutorial series [2].
My React/Redux links list has a large section of articles discussing good Redux architecture and best practices [3]. Some of those articles might be helpful.
Finally, I'd be happy to spend some time discussing your current app's codebase, and perhaps offer some suggestions on ways you can make it better or easier to deal with. HN isn't a good place for that. However, I spend most evenings hanging out in the Reactiflux chat channels on Discord. Please feel free to ping me there, and we can discuss things further. The invite link for Reactiflux is at [4].
[0] http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao... , http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao...
[1] https://github.com/markerikson/redux-ecosystem-links/blob/ma...
[2] https://github.com/markerikson/project-minimek
[3] https://github.com/markerikson/react-redux-links/blob/master...
Redux isn't for everybody or every use case. However, I do think it's frequently misunderstood. If it _really_ isn't helping you solve your problems, then you should certainly use something that's more appropriate or useful. But, it's also entirely possible that a better understanding of how it can be used, or how it can be adapted for certain situations, might make it more useful and helpful for your use case. A lot of my time writing about Redux has been spent trying to help clear up misconceptions, answer questions about real-world usage, and help people better understand how it works and can be used, so that they can make more informed decisions about Redux.
Dan's post "The Case for Flux" [0] discusses what kind of use cases would benefit from a Flux architecture. That still very much applies to using Redux as well, and is worth reading. The Redux FAQ also has a section on "When should I use Redux?" [1]
There's nothing _wrong_ with passing down values as props, even through multiple levels - it's just that most people dislike doing so. You could make the values available using React's "context" feature, although it's likely that wouldn't be the best choice here. There's also a variety of "not-quite-Redux" libraries out there that try to simplify the interactions with Redux ("no reducers", "no dispatching", "no actions", etc), and perhaps one of those might be useful.
[0] https://medium.com/swlh/the-case-for-flux-379b7d1982c6
[1] http://redux.js.org/docs/faq/General.html#general-when-to-us...
Not coincidentally, most blogs about Redux jump right into how to use it, without prefacing it with why would you want to use it in the first place.
A lot of developers think introducing more libraries and more rigor is a good thing even if it drives up the cost of software development. This is the only reason I can think of, for why so many people are using Redux. Another reason may be ReactRouter which makes it harder to supply data to React. But there are other MVC routers that make using React painless and I'd recommend to anyone considering using ReactRouter/Redux to consider better MVC-based alternatives. (And no, using MVC does NOT imply two-way data binding.)
I do agree that many people and articles talk about the "how" rather than the "why", but I'd say that this is a widespread issue rather than being specific to discussions of Redux.
Dan Abramov, Redux's creator, has said himself that "Redux is over-hyped". I also agree that many people are being pushed to learn it without understanding why they ought to use it, and that new learners are being told they _must_ start with Redux and React at the same time. That's not what we encourage officially - we suggest that people start with React first, then learn Redux later.
However, I think saying "people only use Redux because it's hyped or they're told to" is both inaccurate and unfair. I've talked to many people who have said that Redux is helping them write better-architected applications that are more maintainable and easier to work on. Some people may have chosen it due to hype, but many others have chosen it because it solves their problems.
Finally, I'd like to recommend reading Dan's article "You Might Not Need Redux", which discusses the tradeoffs involved with Redux, both the limitations it asks you to follow and some of the potential benefits you get in return: https://medium.com/@dan_abramov/you-might-not-need-redux-be4... .
What percentage of business applications would benefit from this? My guess is, very few. Typical business applications fetch data from the backend and display it, let the user update the data and immediately send the updated data to the backend. There is no need for something to track "when, why and how your state was updated and what part of the application triggered the state update". I am not saying nobody needs that kind of capability. I am saying that few business applications do, and yet there is this notion that React and Redux go together and if you're going to use React it would be strange to not use Redux. Practically everyone who uses React also uses ReactRouter and Redux.
The end-result of this is that React, which by itself is a simple, elegant and useful technology, has been turned into an over-complicated mess by the community of developers, by gluing it with over-complicated add-on technologies.
If an app is really _just_ fetching data, displaying it in a row or form, and sending it back, then there might not be a lot of benefit. However, if data is being shared across multiple pieces of a UI, and the user is interacting with that data in multiple ways, then there may be more benefit.
And yes, my own estimates are that roughly 55-60% of React apps are using Redux as well.
I mean, I've seen people drop redux into a single stand alone form for no reason other than they hadn't used react before, so they picked up a tutorial and the first thing it said to do was use redux.
So, I'll happily agree that there's a whole lot of people using this without any need for it.
...but there is a need for it for some people, and for those people, what's your alternative? Raw react? Have you actually tried that? Passing all the props down from child to child to child to child? It's not awesome, I assure you.
Are there redux alternatives out there that solve the same 'single source of truth' problem I've never heard of?
What are you using?
> There is no need for something to track "when, why and how your state was updated and what part of the application triggered the state update".
...or is that simply not a problem that some people have? I'll certainly agree to that; but then, why are you using react?
If your UI isn't dynamic, why are you building an SPA? For fun? The good old MVC tech stack works perfectly well for static data display and forms.
You know why react exists? ...because complex interactive forms are hard to do right, and because interactive user interfaces that do something more complicated than CRUD operations are actually quite difficult to do right.
The MVVM pattern with data binding is the 'best' solution out there for most UI application framework, and react is successful specifically because it allows you to manage complexity by creating a hierarchy of components instead. That's a really powerful effective technique, there's no question.
Redux lets you manage the state for that hierarchy in one place. It lets you build large react hierarchies and know exactly what state the UI is in at any point in time, in one place. Why? ...because managing state in 100 different locations is complicated.
What you're seeing is tools to help manage complexity; but you only need to use them when your complexity reaches a threshold that triggers the need to use them.
If you don't have a complex problem, you don't need complex tools to manage it.
> has been turned into an over-complicated mess by the community of developers, by gluing it with over-complicated add-on technologies.
What you're seeing is the application of tools to solve a problem that doesn't exist for the specific domain you're looking at.
That doesn't mean they don't solve the problem; it just means your problem doesn't require that level of complexity management.
It's quite frustrating.
I hear the 'its too complex' as an argument from so many people about using libraries and tools, and want 'something simple' instead; but I've hardly ever heard someone pitch a viable alternative instead, just complaints. Too much tooling. Too much complexity. Over engineering.
So, tldr: When you do have a big complex react application with complex UI interactions... what's the alternative?
If there is one, I'd like to hear about it.
I honestly think its not that bad. Elm essentially does that, and its arguably more productive than React's ecosystem.
You might just want to optimize things a bit by making non-leaf components take a simple "model" prop (that contains all the properties they need) to reduce friction a little, but aside for that it might even make code better by forcing a more thoughtful component hierarchy.
After all, if component A renders component B, then A really DOES depend on B's props. Magically having B kinds of hide dependencies.
After all, short of using DI containers, if I have a function that calls another function that calls yet another, I DO pass arguments all the way down. And React components are semantically just functions.
That's a good approach, seriously. You don't need redux at that point; don't use it. I'm not even suggesting such applications are 'trivial'; they probably just don't have excessively complex component hierarchies and interactions.
...but please read this really quite old article if you haven't before: http://blog.ploeh.dk/2012/11/06/WhentouseaDIContainer/
This is an argument that has literally been made a hundred or more times before, and nothing has changed about it.
Sure, this article is about dependency injection, but its essentially the same domain. You can argue that manually passing props is the same of 'poor mans' dependency injection, and it certainly has value in that there's 'no magic' in it.
...and certainly, you fall into the pit of pointlessness and frustrations when you use magic, but there's no convention to make the magic 'all work without surprises'; but the same issues still apply.
Once the application reaches a certain size, the maintenance burden of the 'simple' approach becomes prohibitive.
/shrug
A lot of these solutions, like static typing in non-performance areas, and so on, are about "how do we get this new engineer to start working on code that he has no idea about quickly". This problem only exists in large organisations and I personally don't think large organisations are very good at anything so don't care about those set of problems.
Basically it comes across as another "how do we hire large numbers of average programmers" problem and the facebook talk said exactly that. State managers, like dependency injection, just add another level of indirection that make your application harder to debug. You can no longer just follow a simple chain of methods, because everything disappears into the magic land of the state manager as soon as you start using (as the vuex example) store.commit("increment", 10); instead of actual methods and so on.
If you still don't see any value in these tools, don't use them.
All I'll say is this:
In my experience, people tend to consider their applications to be simple enough that a simple solution is sufficient.
However, in reality, that seldom seems to be the case.
UI is hard to get right, and has a tendency to drift towards complexity over time as more features are added: I can think of several people who have picked the naive simple solution and regretted it later; to the point of rewrite in some cases.
People who picked the complicated solution sometimes regretted the extra burden of complexity, but I don't think I've ever seen this as a cause for large scale failure or rewrite.
the order of the modifications
who performed them
how they modified them
So the state manager adds that in as well. You can add getters and setters and enforce synchronous programming in certain methods, but then you are 90% towards a state manager.
https://github.com/tjdavies/copal
Its basically the same as Redux in intent but nicer to work with. It even supports the redux dev tools
I'd love to hear contrary experience.
Specifically, 1) it makes rapid iteration simpler because I don't have to click back to the portion of the app that I'm testing but can instead just rehydrate a cached state, and 2) it makes it trivial to save state for the user either by caching it locally in the browser or by sending it over to the back end.
This.
I'm working on a fairly small but still nontrivial React app for work (it's the first time we've used React for anything here, and we're partly doing it to evaluate React for future work). I've not used Redux anywhere in my application (it's all vanilla React), but my state is still all in one place in an organized format, and, since I have a single component managing my state, I can still trivially do both (1) and (2) there.
What's Redux giving me that I don't already have, aside from another layer to maintain and another piece to break?
Quick summary: you don't have to pass data as props all the way from the top of your app to the bottom, and it lets you keep your app state if you're doing hot module reloading in development.
In addition, time-travel debugging and the action history log really are powerful tools for understanding how your app is behaving over time.
My other comment just below this discusses some more benefits, and links to Dan's "You Might Not Need Redux" post. I'd encourage you to read that as well.
Quick note, you can accomplish this directly using the "context" feature of React, which is what Redux uses under the hood anyway.
The React docs generally advise that apps try not to use context directly, as the API has limitations, and _will_ change in some future React release once they figure out a better approach. The main issue is that context updates are blocked if a component returns `false` from `shouldComponentUpdate` (and the same for a PureComponent). As a result, context is best suited for references that won't change, such as pubsub event emitters.
Michel Weststrate's post "How to safely use React context" [0] is excellent, and Ken Wheeler just gave a good talk about context at React Boston [1].
[0] https://medium.com/@mweststrate/how-to-safely-use-react-cont...
Contexts is useful for anything that rarely changes. Like callbacks. But if you want to send down stateful things it will quickly become a mess for complex apps.
eh. Not really. Because there is no schema, what happens is the Redux store turns into a mess.
You got 10, 20, 30 developers working on an app, no one has a clue what any of the Redux variables mean or what use-case they are for (or what use-case they may change), and worse you have redundant data scattered everywhere and you're never sure which should be the authority. It's an ad hoc global variable soup that is updated with the moral equivalent of GOTOs (Redux actions) that litter the code in just about everywhere you could possibly think of.
The TL;DR is Redux tries to place a set of constraints on how and when you're allowed to share state between components and how and when you're allowed mutate that shared state, much like how React and it's virtual DOM abstraction places constraints on how and when you're allowed to manipulate the DOM.
These constraints allows the library to make certain assumptions about state and state changes that enables it to perform global performance optimizations across the entire codebase (namely performing only shallow equality checks on all state-mapped props when determining whether or not to rerender, because it can assume all state changes will result in a reference change) that would otherwise have to be performed for each component on a case by case basis, and micro-optimized by each individual developer by hand.
These constraints also allows tooling authors to make these same assumptions, which is what enables the best-in-class tooling that Redux developers have access to, like time traveling debugging and state change replays. These tools would simply not be possible to build in a generalizable way if your codebase didn't allow them to make the same assumptions (and adhere to the corresponding constraints when it comes to state sharing and state mutations) as it could for a Redux codebase.
The post has a whole list of very compelling use cases/tooling that are either made possible by or at least made completely trivial by the enforcement of these constraints around state mutations:
> These limitations are appealing to me because they help build apps that:
> Persist state to a local storage and then boot up from it, out of the box.
> Pre-fill state on the server, send it to the client in HTML, and boot up from it, out of the box.
> Serialize user actions and attach them, together with a state snapshot, to automated bug reports, so that the product developers can replay them to reproduce the errors.
> Pass action objects over the network to implement collaborative environments without dramatic changes to how the code is written.
> Maintain an undo history or implement optimistic mutations without dramatic changes to how the code is written.
> Travel between the state history in development, and re-evaluate the current state from the action history when the code changes, a la TDD.
> Provide full inspection and control capabilities to the development tooling so that product developers can build custom tools for their apps.
> Provide alternative UIs while reusing most of the business logic.
You could certainly make the case that Redux is not necessary for building a React app. Or the case that the additional boilerplate and constraints it places upon you could be a net negative for apps that are not sufficiently complex to warrant them. In fact, that's exactly the case that the linked post tries to make.
And yes, the only reason people use Redux is to introduce rigor (constraints) into their codebase and development workflow. But to insinuate that the rigor and constraints introduced by Redux will only ever be a net negative in terms of developer productivity is to display a fundamental ignorance regarding the vital role that rigor and constraints have always played in software architecture, and the fact that the recent advances in frontend architecture that have been proven to reduce complexity and make large codebases easier to reason about (things like the virtual DOM, unidirectional data flow, immutable state trees/caches, functional/reactive programming, and even the MVC pattern itself) have almost always come as a result of placing more constraints on what the developer is allowed to do.
I've touched probably 200+ production frontend apps of various sizes by now, ranging from a couple of pages, going to tens of millions of lines of Javascript in a single monolith.
Your millage will vary and everyone hits different challenges in their career, but to me, the biggest issues when building apps was never building them from scratch or adding new features. It was always maintaining existing features. So I optimize everything I do for that, as I consider everything else completely trivial except for groundbreaking algorithms designed by PhDs (which is not me) and not worth thinking about.
I've spent years trying to nail down why these apps, no matter who works on them, always end up impossible to deal with. No matter how much time is spent trying to figure out the architecture, documentation and design, it always goes to hell given enough time.
To me (and its a totally personal analysis), it was always nailed down to figuring out: "Where the F* does this value come from" or "how do I follow what the hell this button does". It should be simple. I can use a debugger, obviously, but as requirements pile on, it invariably goes to hell.
Elm, to me, is the panacea of solutions here. A single atomic state, and a UX that is nothing more than a projection (or a map, if you prefer) of state to UX.
And to keep the UX "dumb", you then need to externalize all state and side effect logic.
Once you have that, you can reason about any bug without even looking at the code. Is the UX wrong? Was the state right? No? Did the event triggered right? yes? Well, the bug HAS to be in the update function. Boom done.
Now the question becomes, how do you reproduce this in JavaScript, which doesn't even have an approximation of algebraic effects, doesn't have immutable data structures, doesn't have a good way to play with the dom in a declarative way...
React + Redux with a few extras from the ecosystem provide that. Who cares about boilerplate, I get predictability. The more you abstract, the less predictable things get and bugs can come from anywhere.
Inevitably as the app grows, you need to add stuff and weave it in the existing features. The decoupling of State <--> Selector <--> Component <---> Action <---> Reducer has a very tightly defined set of semantics where even the most complex requirement fall neatly in these atoms (not quite as neatly as Elm when it comes to side effects, but it's as close as I've found for now).
Build an app some other way that does the -exact- same thing? You'll either end up with just as much code (oh, it won't be as repetitive and boring, but it will be there. I LOVE boring code), or you'll end up with something much more rigid that requires more compromises. There's a few alternatives that get close, but they usually do so by tightly coupling concerns and making it harder to figure out where the hell shit is happening.
The only obstacle is that the reasons behind "why" things are done this way is almost lost to most people working with Redux, in spite of Mark's best effort to prevent it from happening (there's only so much he can do short of brainwashing the planet). When the "why" gets lost, the community starts building things that neuter Redux, making it seemingly useless vs the alternatives.
I feel the pattern has helped as the team has grown. Yes there is boiler plate, but that boilerplate makes it hard to deviate from the pattern e.g. if you want to update the application state, you must dispatch an action, no way around it. In the past with MVC/MVP style patterns responsibility get muddled and you get a lot of inconsistency in implementation.
I've got no data for it, just observations, but since we have switched a to more functional style of programming and using immutable data, we are encountering less bugs which I put down to side effect free code.
Overall we have had a positive experience with React + the Redux pattern. Yes you could do this with other frameworks and patterns, but it works well for us and no doubt the same argument could be applied to whatever framework others are currently using.
[1] www.barvas.com
Even for those small applications/prototypes, having a centralized store allows me to code on auto-pilot where I don't have to worry as much about the code and focus on what it does instead. Would X particular piece of UI work better in a different way? Okay, then that UI code can fuck right off; it's not like it's going to affect the rest of my application's state.
I used a Flux library before but like mobx now and will default to that unless some specific requirement point to redux or something else.
I've definitely noted that React devs seem to fall into either a "component-centric" view of the world, or an "app-centric" view. Both are okay.
I'll also point out that the Redux FAQ explicitly says it's up to you as a developer to decide what state should live in Redux, and what is better suited for local component state: http://redux.js.org/docs/faq/OrganizingState.html#organizing... .
It seems like it is essentially trying to bring functional union type case switching to a language without that feature. As such, actions are insanely laborious to construct. Furthermore React / Redux is far to pedantic in reinforcing it's design decisions without offering a backdoor to work interact with the outside world.
Which design decisions are you thinking of?
Mostly that updates can only happen on explicit changes to the state. Also a React application doesn't acknowledge the possibility of a component existing which is not a React component. I most recently ran into this issue when trying to add an OpenLayer map to an application. In a vanilla JS application it's trivial, but React was difficult to coerce into breaking it's own rules to accommodate it.Elm solves this particular issue well with Ports [1]. It respects the possibility that there may be things going on that haven't been explicitly planned for.
actions are insanely laborious to construct
On a second read through, this is edging on hyperbole. Maybe a more honest presentation of the facts is that Redux actions grow tedious to write. You start with an action type declaration: export const SOME_ACTION = "SOME_ACTION";
Then you create an action creator: export function performSomeAction(data) {
return {
type: SOME_ACTION,
data
}
}
And then of course you need to write a case statement in a reducer to deal with the data and somehow effect the state.It's tedious because it's trying to recreate the pattern matching functionality of passing union types around in functional languages. In SML, Haskell, Elm etc. you can elegantly decompose and consume types with case statements and pattern matching, which are built into the languages [2, 3].
[1] https://guide.elm-lang.org/interop/javascript.html [2] https://stackoverflow.com/a/14604042 [3] http://www.cs.cornell.edu/courses/cs312/2004fa/lectures/lect...
Per the "non-React components" aspect: wrapping up non-React code into components is actually something React is pretty good at. The typical implementation of a maps lib wrapper component would have React render a single div with a ref, then use `componentDidMount` to initialize the plain JS lib and hand it the div. From there, the lib can attach any extra elements it wants, and React won't care. React's lifecycle methods like `componentWillReceiveProps` and `componentDidUpdate` are used to call the imperative lib APIs, and the lib would be cleaned up in `componentWillUnmount`. I wrote a blog post that demonstrates using React components to wrap up the Cesium 3D globe library [0], there was a great talk at ReactBoston that shows doing the same for Mapbox [1], and I have links to other similar articles about interacting with non-React code [2].
Per my other comments elsewhere in the thread, it's up to you how much abstraction you want to use for actions and reducers. A common suggestion is the Redux-Actions lib, which provides utilities for defining action creators and reducers. You can even use the action creators themselves as the "constants" for the reducer cases, since they have custom `toString()` methods.
Also, switch statements aren't a requirement either - they're just the most obvious way of handling multiple values in a single `action.type` field. You can write whatever conditional logic you want in a reducer.
All that said, I do agree that the JS language as a whole does not have the built-in pattern matching capabilities that other languages have.
[0] http://blog.isquaredsoftware.com/2017/03/declarative-earth-p...
[1] https://speakerdeck.com/shortdiv/supercharge-your-maps
[2] https://github.com/markerikson/react-redux-links/blob/master...
https://github.com/erikras/ducks-modular-redux
I'll also point out that those tools are all listed in my Redux Addons catalog at https://github.com/markerikson/redux-ecosystem-links , albeit nested inside the different category pages, and I did just highlight them all in my ReactBoston talk over the weekend (slides and video at http://blog.isquaredsoftware.com/2017/09/presentation-might-... ).