How Redux Works: A Counter-Example
daveceddia.com
daveceddia.com
I think a pre-requisite to learning something like Redux (or any micro-architecture) is to first try building something without it. Once you understand the pains of undisciplined, organically designed, spaghetti applications, the cynicism is replaced by excitement over how this will improve your job/life/application.
I tried to call this out in the article to make sure people are aware that improving simple Counter examples is not what Redux is really meant for.
I'm considering putting together a larger course/book that does just what you say - build up a React app using plain state until it becomes painful, and then refactor it to add Redux. My question right now is, how far should I go? Do I walk through building the entire app with state (and push through the complexity), or do I give up at the first indication that Redux would make things easier?
I'd love to see a complete project that uses redux, and would be horrible without it. Any pointers?? :)
- An 8-part "Build a Simple CRUD App with React and Redux" tutorial: http://www.thegreatcodeadventure.com/building-a-simple-crud-...
- My own "Practical Redux" tutorial series, which shows off specific useful React and Redux techniques within a sample app: http://blog.isquaredsoftware.com/series/practical-redux/
Also, my Redux addons catalog has a page listing many other interesting Redux-based apps, including purpose-built samples and real-world apps: https://github.com/markerikson/redux-ecosystem-links/blob/ma...
IMO, It's a fundamental flaw with JavaScript that interfaces aren't rigorous or discoverable and redux is a willing victim.
Is this a concern with docs, the core Redux store API, the React-Redux API, or something else? Any suggestions for how we can improve things?
I don't have a fix, but I think just being able to inject a link to the store and then having an API that can be manipulated directly from the component would more comprehensible and cut down on the layers of indirection. I typically see corresponding action, reducer js file for a component when 95% of the logic is already in the component. Just merge them.
It's also worth noting that I personally highly recommend consistently writing separate action creator functions [0], and using the "object shorthand" argument to `connect` instead of writing a separate `mapDispatch` function.
That said, it also seems like part of what you're concerned with is the fundamental design of Redux. The use of plain object actions as a layer of indirection is deliberate [1], and it's what enables capabilities like time-travel debugging. Actions need to be serializable for that to work, and therefore strings are the best solution for the action's `type` field [2].
You certainly don't have to keep _everything_ in Redux. You should consider whether a given bit of state should live in Redux, or in a component's state [3]. But, if you _are_ going to keep data in Redux, then actions and reducers are necessary for the Redux data flow. They don't have to be in separate files [4], but they need to exist to update the store.
[0] http://blog.isquaredsoftware.com/2016/10/idiomatic-redux-why...
[1] http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao...
[2] https://redux.js.org/docs/faq/Actions.html#actions-string-co...
[3] https://redux.js.org/docs/faq/OrganizingState.html#organizin...
[4] http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao...
I'm not being a grump either. I coded Perl professionally for over 5 years and am well-versed in terse, functional idioms, but the community rightfully decided that strings of punctuation were too obtuse for practical use.
I'm hesitant to see new programmers avoiding a "learn the hard way" project. Without it, there's so much they might never grok.
Beautifully said. The same goes for schema-less data models. The full appreciation of foreign keys, data type validation, and check constraints doesn't kick in until you try incrementally changing a NoSQL spaghetti app.
Redux, on the other hand, expects you to dive into the deep end head first the first time you use it. I teach javascript development, and I'm noticing there's a pretty big gap where you're feeling the pain of React, but Redux is still too large a complexity tax (To be fair, even to an experienced programmer it often is - for every complex state change that warrants Redux there's 50 straightforward "change this field" or "toggle this boolean" actions.)
Maybe this isn't the responsibility of Redux, and something should be built on top of it, but there needs to be some amount of consensus on what the best solution is.
It's a shame I can't really teach it, however - the fact is, it doesn't have enough mindshare right now and part of the reason people take courses is to learn marketable skills. For better or for worse, nearly every React shop is looking for devs who know Redux.
You might not need Redux:
https://medium.com/@dan_abramov/you-might-not-need-redux-be4...
The little example he provides is remarkably powerful and a great intro to thinking like a Redux programmer.
I tolerate it for basically "no-one got fired for buying IBM" reasons, i.e. the devs I work with think it's good and it's better to have twice as many problems and none of them be my fault than half as many and all of them be my fault.
Also, side note, it's hilarious (and maybe telling) that we both referenced the "no-one got fired for buying IBM" quote...
Having come from cross-plat C++, the JS build system(s) are impressing me, at least the one around React Native.
One data point. :)
> there needs to be some amount of consensus on what the best solution is.
The thing is, there is consensus: it's Redux. I'm not saying that's good or bad, but it's king of the state management hill right now.
What's really interesting to me is that the JS community is largely allergic to anything that isn't the "best solution". Nobody wants to use a little library with 53 stars on Github, even if it's easier, or seems like a better fit than Redux. The thinking seems to be "It's not any good unless it's the most popular choice." Those sorts of libraries might be ok for proof-of-concept work, but not for the real world. Not for production.
What drives this line of thinking? I think it's complex, but I believe fear plays a big role.
- Fear of choosing the wrong library (what if it becomes unpopular!)
- Fear in one's own abilities to evaluate a library ("it seems fine to me, but I'm no authority, I can't make that call")
- Fear of looking bad to the boss ("Nobody ever got fired for buying IBM", right? React + Redux has that spot right now)
There's also the self-reinforcing nature of a popular thing: companies using Redux => job listings require Redux => people want to learn Redux => people make Redux courses/books/blogs => people don't teach the alternatives.
- More popular libraries are better maintained and have more support
- More people use them (obviously), so more problems will already have already been solved the ${library} way
- Much easier to hire developers who know the more popular libraries
We don't plan to modify the Redux core itself, but I would _love_ to be able to list some tools as "approved" or "blessed" ways to get started easier.
Sadly, I just don't have enough free time to go build much of anything like that myself, but I will happily work with anyone in the community who's interested in doing so.
I also hope to revamp the Redux docs "Ecosystem" page to more specifically point to certain tools as recommended solutions to specific problems, but again haven't had time to do so yet.
The discussion trailed off, but I'd love to get additional feedback and ideas on ways we can improve things and build easier-to-use abstractions on top of Redux. (Or, even better, get some volunteers to _help_ us build better tools for getting started.)
Which it is, actually!
Think about how MVC works in iOS, or ASP.NET MVC or JSP Model 2, etc. In the case of the latter two, your state is stored in the session as simple, regular objects. Have you felt the need for actions and reducers and immutability etc. when using session state? I have not.
When programming JavaScript SPA, you can program in the style of MVC also. There is no need for actions, reducers and so on, unless your app is for example a word processor and you need undo/redo (as mentioned by another comment on this thread.) Most of the time you are getting data from the backend, temporarily storing stuff in memory, modifying it and sending it back to the backend. There is no need for actions/reducers and such other nonsense for that.
When I mention this someone always points me to the "you may not need redux" article by the creator of redux, in an attempt to legitimize some use cases of redux. There may indeed be some legitimate uses cases for redux. But it has become the de facto standard way of building React applications, and that's totally unjustified.
Disagree. There is no reason to believe MVC tends towards spaghetti in large codebases. Controllers and views implement a small portion of the application's functionality. When the applications get larger individual controllers and views do not even know that the application got larger, so this scales very well.
I've worked on some Angular (1) apps that became very messy over time. I think it all depends on the project. React/Redux can get messy too.
It doesn't because encapsulated mutation does not compose. Typical example: your data model fires signals notifying change as soon as you change them, then thew view updates. All good, until you have to build transactional updates and then you get flicker, broken invariants due to partial values being propragated, etc, etc. You need to understand how everything is wired up together to really know what is going on because effects are interleaved with logic, even when each component individually feels decoupled and nice.
What Redux/value-based data model does is to really separate those concerns. Your update logic is a function that just can mess all it wants with the new values it produces. It is composable because you can just build logic by calling smaller logical unit and you know everything you need to know by looking at the inputs and outputs.
It is way simpler (and less!) code in all but the most trivial of the examples.
I give in that the tricky part is finding UI frameworks that play nicely. You need something React-like to efficiently re-evaluate the Model -> UI functions, or use an immediate-mode API. Alternatively one can manually write some function to update a stateful UI by comparing the previous and current model and often it's enough. It is a bit like your controller but it is agnostic to the specific actions that happened but just look at the model changes (i.e. no global knowledge needed). I recently discussed this here:
https://github.com/arximboldi/lager/issues/1#issuecomment-34...
That is a very narrow definition of MVC. I am talking of large interactive software with multiple views on large hierarchical data trees data with different levels of focus and granularity. The kind of software I am thinking of big programs like Photoshop, Premiere, an IDE, Ableton Live...
Disclaimer: I worked for Ableton for quite a 5 few years, and have spent most of my career building interactive software (I work as independent consultant now). Most of the software of that time and age is built around MVC. It kinda works, but as a lot of people in the industry building software of that size will tell you: scale brings the the pain. Because of composoability. In the C++ world, see also talks from Sean Parent--who worked on Photoshop--and is also a strong proponent of "value-based" approaches (he is a great source of inspiration for me). Sean claims that something like 70% of bugs in Photoshop are in UI-related code typical MVC wiring up stuff.
In the end, just pick the right tool for the job. If you are writing small apps and MVC does not explode at that size and fits nicely with the underlying frameworks, go ahead and use it (I strongly suspect you are an iOS dev and understand how conformity with the native framworks is very important there).
But if you are building something large, highly interactive, with lots of views and concurrency, try to move to a more declarative/unidirectional flow/functional approach. It is still an open field and there are many alternative approaches, but it builds down to composability, denotational reasoning and decoupling of effects/logic.
Since then a lot of different things have been called MVC..
I wouldn't mind a use case where Redux would be necessary or at least beneficial, for the practice, but I've yet to run across it after building half a dozen or more SPAs. These would be enterprise SPAs used in production, btw.
https://github.com/kriasoft/react-starter-kit
Redux-friendly, too.
It's been much easier to pull all app state into a central, db like structure, and allow components to connect and query that structure for whatever data they need. It makes making changes to components in the future easier, because all of my data is normalized in a structure, and it's all possibly available to any component in the app.
I've found that with MVC, if you design the private state a certain way, and in the future need largeish independent components to coordinate, well, you're going to have a bad time.
So redux or not, I'm definitely a fan managing state as it's own separate thing, and having components able to query and connect to that data structure, without having to pipe props down a tree of components, which leads to an app that's hard to change.
What this will let you do is basically extend your server-side GraphQL API with a set of local resolvers for queries and mutations, and let you query and mutate both your server-side and client-side state using a single consistent API (GraphQL, with client-side fields decorated with a @client directive), backed by a (mostly) automatically normalized local state cache (by default apollo-cache-inmemory, but customizable, so you can even implement a Redux based cache if you'd like more control).
A lot of the complexity in Redux today stems from the need to reconcile client-side state with server-side state, usually fetched through some kind of async actions paradigm like Thunks+Promises, Saga, Observables, etc, to be then manually merged into the local state after being transformed into a normalized form.
I personally think one of the biggest problems with the design of Redux is that the architecture derives most of its benefits from having a normalized state tree, but it also makes it all too easy for developers to store non-normalized data in the state tree. Although admittedly an architecture that does enforce a normalized state tree would be an order of magnitude more difficult to learn and work with in the wild-west of RESTful APIs that used to be the de-facto norm back in the days when it was designed (and it still is, though that may be slowly changing with the rapid adoption of GraphQL), where most server-side data you receive isn't easily normalizable to begin with.
A good GraphQL client like Apollo can already abstract away much of the complexity involved with async server requests through its Higher-Order Component-based API, which simply provides data to the wrapped component as props, so it doesn't need to know how to fetch that data. More importantly though, Apollo also ensures that all the server-side state it maintains in its cache is stored in a normalized manner, and GraphQL makes this fairly easy.
Apollo-link-state goes a step further and allows you to use the same abstractions for managing client-only state, and store it in a normalized form alongside your server-side state, to allow for a much more consistent and comprehensive state management API.
With all that said, apollo-link-state is still really young and I've only played around with it briefly in toy projects, so there's no guarantee it could scale as well to large teams as Redux has. I do still personally find it to be one of the most promising alternatives out there though.
Lastly, Relay Modern also supposedly has something called Client Schema Extensions, which I assume is similar to apollo-link-state, but wasn't able to find any documentation on it outside of a brief mention in its release notes: https://facebook.github.io/relay/docs/new-in-relay-modern.ht...
> Dan Abramov, just now : "if you want to teach someone why to use an abstraction, you should first make them feel the pain of not having it"
But yes, it's tough to try to show the value of Redux while at the same time keeping the example simple enough to illustrate the basic mechanics.
I have recently been working on a Redux for C++ experimental library with time-travel and all. As an example I built a mini-emacs text-editor with it. Should post about it here whenever I flesh out the README and all (or the MeetingCpp talk about it gets published). In the meantime you might want to check these links:
The library: https://github.com/arximboldi/lager
The Counter-Example: https://github.com/arximboldi/lager/tree/master/example/coun...
The text editor: https://travis-ci.org/arximboldi/ewig
Show-case of a time-travelling debugger: https://twitter.com/sinusoidalen/status/926382341433577472
You're not wrong, but practically 99% of people pair it with react.
https://github.com/search?utf8=%E2%9C%93&q=angular+redux&typ...
Redux can be used with any UI layer, and there's bindings for many frameworks and UI layers besides React [0].
Redux has picked up popularity within the Angular community, both in its original form and the NgRx reimplementation. The ember-redux bindings are also starting to gain a bit of a following. Vue's VueX lib is inspired by Redux, and there's Vue-specific bindings for Redux itself.
[0] https://github.com/markerikson/redux-ecosystem-links/blob/ma...
In any case, it's led to a ton of confusion in the community, especially for newcomers.
That said, I found the reverse approach to Redux to be more confusing than the actual Redux documentation and the tutorials that Dan Abramov has already put together around Redux which explains things quite lucidly.
Example: https://egghead.io/lessons/react-redux-the-single-immutable-...
Second - it's interesting you found the reverse approach to be confusing. I've heard from multiple people that loved it and said it made more sense to them that way. Different strokes and all that!
How experienced were you as a dev before you read the Redux docs + Dan's tutorials? I ask because I learned with those too, and they made sense to me (Dan's egghead videos were eye-opening), and I've been doing software for > 10 years professionally... but my suspicion is most beginners don't do as well with the top-down approach.
The thing I found so compelling about Redux was thinking about state being immutable unless you took the original state {...} and changed with a reducer, which was called depending on the action you want to take. At that point, when something is changed, the whole app is changed - it's the one thing to rule them all. If you want to change something, you need to take action. "How do you change state?" "Take an action."
Without understanding that flow up front it was hard to determine _why_ one would need to do things like connect, createStore, etc and how state magically got to be a prop.
I think your walk-through does a great job of explaining things step by step starting from the react code (and what Redux replaces) and I can see it working well for a number of learning styles. I also really like the pancake syrup provider imagery.
I've seen feedback that Dan's teaching style is the greatest thing ever, and feedback that it requires a giant leap of faith that "we write thing A, and thing B, and thing C, and magically they all work together at the end". Clearly, different people have different learning styles. Dan and Andrew _did_ try to write the docs in a way that would be accessible for people with varying backgrounds, but it's tough to write docs in a way that works for everyone.
If you or anyone else has suggestions for improving the docs, please let me know! I'm always open to ideas for improvements. Please ping me on Twitter at @acemarke, or file an issue / PR in the repo.
[0] https://redux.js.org/docs/advanced/Middleware.html
[1] https://github.com/reactjs/redux/pull/140#issuecomment-11371...
[2] https://twitter.com/dan_abramov/status/622568094939090944
I also saw a similar "React and Redux: An Introduction" tutorial published just within the last couple days [0].
For anyone who's looking to learn more about Redux, I'd encourage you to check out my React/Redux links list [1], which has sections pointing to more Redux tutorials [2], Redux architecture and best practices [3], and much more.
If you've picked up the basics of Redux and want to see how it works at a larger scale, I recommend the "Building a Simple CRUD App with React and Redux" series [4], and my own "Practical Redux" tutorial series [5].
Also, my "Idiomatic Redux" blog series [6] looks at things like the history and intent behind Redux, common usage patterns and why they exist, and why I consider certain patterns to be the "right" way to use Redux.
Finally, I am always happy to answer questions about learning and using React and Redux! Please feel free to ping me on Twitter at @acemarke. Also, come by the Reactiflux chat channels on Discord [7]. There's always plenty of people around who can answer questions. I'm usually on there evenings US time.
[0] http://jakesidsmith.com/blog/post/2017-11-18-redux-and-react...
[1] https://github.com/markerikson/react-redux-links
[2] https://github.com/markerikson/react-redux-links/blob/master...
[3] https://github.com/markerikson/react-redux-links/blob/master...
[4] http://www.thegreatcodeadventure.com/building-a-simple-crud-...
[5] http://blog.isquaredsoftware.com/series/practical-redux/
[6] http://blog.isquaredsoftware.com/series/idiomatic-redux/
That said, I'm already very busy just trying to keep up with the React+Redux community, and don't have time to keep tabs on everything that's going on in the Angular world as well. If you do find any kind of similar collection that's more focused on Angular+Redux, please let me know!
If Dave's post was scoped to optimizing redux implementation, detailed code samples would be appropriate.