Redux: Request for Discussion: Redux "boilerplate", learning curve, abstraction, and opinionatedness

Created on 19 Mar 2017  ·  108Comments  ·  Source: reduxjs/redux

Resolution: use Redux Starter Kit

The ideas in this thread eventually turned into our new Redux Starter Kit package. It includes utilities to simplify many common Redux use cases, including store setup, reducer definition, immutable update logic, and even creating entire "slices" of state automatically without writing any action creators or action types by hand.

For more details on what problems Redux Starter Kit is meant to solve (and what it _won't do), see the "Vision for Redux Starter Kit" manifesto I wrote .

The number one complaint I see about Redux is that there's "too much boilerplate". I also frequently see complaints that there's too much to learn, too many other addons that are needed to do anything useful, and too many aspects where Redux doesn't have any opinion and therefore doesn't offer any kind of built-in guidance.

I just had a discussion with @tannerlinsley regarding several of these aspects. We discussed the Jumpstate library, which is an abstraction layer around Redux, and how it was intended to ease the learning curve, as well as various philosophical opinions about what constitutes "good" Redux usage.

Out of that, we came up with several questions that I would like to bring up for wider discussion:

Key points

Boilerplate / Verbosity

  • Redux is not intended to be the "most concise way of doing things", but rather to make data flow obvious and readable
  • Docs are written in a deliberately verbose style for clarity and learning, and not specifically intended as "the one true way to write Redux code", but that style has been somewhat blindly adopted (or, sometimes, resulted in rejecting Redux)
  • Frequent complaints about "boilerplate", such as "too many files", "use of action creators", etc

Abstractions and Learning

  • The core Redux library itself is effectively feature-complete, but there's lots of interesting addons and tools being built by the community
  • Various abstraction libs have been built to "ease learning curve" or "make things more OOP", but most of these are not really "idiomatic" Redux usage
  • Redux learning curve can be steep, but once you grasp concepts, the need for abstraction layers often goes away

Problems

  • What are the "boilerplate" complaints mostly about?
  • What are the hardest aspects of Redux for new learners?
  • What are the "no opinions" areas that cause problems for people?

Potential Solutions

  • What would idiomatic Redux usage with "less boilerplate" look like? How can we provide solutions to those complaints?
  • What possible abstractions could be created that simplify the process of learning and usage, but without actually hiding Redux (and would hopefully provide a migration/learning path to "base" Redux)?
  • How much of this could be solved with improved docs in some way?

I would like to invite the community to offer up complaints, pain points, and concerns about using Redux, and hopefully also provide suggestions and ideas for solving those problems as well.

Most helpful comment

If it were up to me I’d like to see this:

  • A Flow/TypeScript friendly subset that is easier to type than vanilla Redux.
  • Not emphasizing constants that much (just make using string literals safer).
  • Maintaining independence from React or other view libraries but making it easier to use existing bindings (e.g. providing mapStateToProps implementations).
  • Preserving Redux principles (serializable actions, time travel, and hot reloading should work, action log should make sense).
  • Supporting code splitting in a more straightforward way out of the box.
  • Encouraging colocation of reducers with selectors, and making them less awkward to write together (think reducer-selector bundles that are easy to write and compose).
  • Instead of colocating action creators with reducers, getting rid of action creators completely, and make many-to-many mapping of actions-to-reducers natural (rather than shy away from it like most libraries do).
  • Making sensible performance defaults so memoization via Reselect “just works” for common use cases without users writing that code.
  • Containing built-in helpers for indexing, normalization, collections, and optimistic updates.
  • Having built-in testable async flow support.

On top of core, of course, although it’s fine to brand it in as an official Redux thing.
Also, I won’t be writing this. But you can.

All 108 comments

A few ideas:

  • official redux-preset package containing redux, react-redux, redux-thunk already wired together
  • dispatch(actionType, payload) could reduce need for action creators
  • built-in jumpstate-style reducer creators, e.g. createReducer({[actionType]: (state, payload) => state}, initState)

Most people I know who use redux heavily, end up moving away from redux-thunk as they find it doesn't scale very well, and ends up leading to action creators that start doing way too much, and rather use something else for managing side-effects like redux-observable or redux-saga.

I can understand the appeal in it, especially when getting started with redux - but I'm not sure if bundling it as part of a 'best practice boilerplate package' would be the best idea.

So, this part is the more critical to me (this will not so much be ideas to solve the problem, but rather constraints that are, at least to me, important):

Redux is not intended to be the "most concise way of doing things", but rather to make data flow obvious and readable

A great thing about Redux is how it's almost more of a design pattern than a framework, and all the code you see (aside for the store and react-redux) is your own.

Looking at Jumpstate (I did not know about it until now), it's one of the first abstraction layers I see for Redux that looks good. Great even! But at the same time, it brings you only a hair away from other frameworks like MobX, and having multiple frameworks that bring in the same thing to the table isn't super useful. So focusing on what makes Redux different from the rest is important, not just how we can make Redux better in a vacuum (because it lives in the same world as other tools).

Another thing of importance, is that a lot of challenges newcomers have when they hit Redux isn't Redux itself, but JavaScript. Other big frameworks like Angular abstract away JavaScript itself. Writing a Redux reducer is simply applying vanilla javascript "design patterns", which may not be familiar to newcomers, and will make people want to use a black box instead.

Finally, a lot of boilerplate reduction libraries try to add a 1:1:1 relationship between components, reducers and action creators (maybe not all 3, but often 2 of those), which makes newcomers forget they can have multiple reducers handling an action, multiple components using state from multiple reducers, and so on. That's one of the #1 question I answer at work and other places, and it's super useful. So tool to help in that area should not lose this (also, "switch are icky" isn't the best argument in the world).

So IMO, CTAs would involve linking good vanilla JavaScript resources, as well as documenting more of the "why" right along the "getting started" concepts, and keeping things simple. You can build gigantic applications in Redux without learning many new concepts at all. While I'm a redux-observable guy myself, I've seen multi-hundred-thousand lines of code apps using Thunk without issues (and Ive seen tiny apps make a trainwreck with thunks). Introducing very few "core" concepts and showing how they can be applied to tons of concepts helps a lot.

The "all boilerplates must be reduced no matter the cost" is an issue with the software engineering community as a whole these days...thats harder to tackle.

From the very beginning my main concern w/ redux was that either I read or write a code, I had to jump between N files, b/c logic of the single UI part is scattered all over the codebase between actions, action types and several reducers. I really like that I can reach out to any part of the state tree from every place in UI and I can change the different parts of the state in response to a single action (main reasons why I use redux), but the more parts of the state I change in response to a single action, the more my logic is blured. I can't simply read or write what's happened when user did this or that. But what I want is can be described like this:

// meta code
dispatch(ACTION);

onAction = {
  ACTION: [
    // handler 1: hide spinner here,
    // handler 2: change status there,
    // handler 3: update entity
  ],
};

In the end I came up with redux-interactions + redux-tree and I'm pretty happy w/ it so far.

Approach in our current project:

note, would be very different depending on application requirements :) once we add in realtime object updates, perhaps sagas or observables would provide benefit over thunk/promise

  • redux-thunk
  • promise middleware
  • our "api wrapper", which is a wrapper around client feathers app, service(name, method, ...opts) which is an action creator with payload being a promise to call our API (i.e. promise middleware picks it up and dispatches x_FULFILLED, x_PENDING, and x_REJECTED). could also overwrite name of action for this.

We first use componentDidMount() to call our API, store data in component state. However, we use our api wrapper for this, which means an action is still sent out (and logged by logger middleware, including meta which is request info), and if we so desire to refactor into using the store for that component all we need to is add a reducer which listens on the action. We start by using local state only, and refactor into redux when that component's state needs to be accessed/modified from another component. That being said, I see the attraction of using redux everywhere, as it provides a paper trail for everything. Given our current team and application timeline, it's just not beneficial for us atm.

I've played with redux-saga a bit, but since our most complicated async flow is login/logout, which works (and code is not terribly complicated), not much of a huge reason to switch (but might be worth it for testing reasons - iterable + mocks is a nice way to test). redux-observable I don't see the immediate benefit unless observables would themselves provide benefit, for example, if you want to double-click events nicely.

We've gotten a bit away from boilerplate by having our own factory functions to return reducers, and having our own higher order reducers on top of custom functionality (e.g. a reducer on top of "paginator" so that content is paginated but can have custom actions to modify an item).

What I think needs to be done is a giant tutorial working up from a basic react app, demonstrate issues that come up with inter-component communication, then introduce redux, then go onwards, demonstrating problems that occur under specific situations, and how they can be helped with redux-x. That is, a guide for when and what to use. There are definitely some blog posts that exist out there with discussion in this direction.

What's also relevant is my summary I came up with for patterns used in gothinkster/react-redux-realworld-example-app

Most people I know who use redux heavily, end up moving away from redux-thunk as they find it doesn't scale very well

I understand what you're saying, but here's my flip-side concern: we're seeing more and more anecdotal evidence that a huge group of JS devs aren't even using _arrow functions_ and other baseline ES(6|2015) features yet, due to lack of understanding, intimidation, etc. Expecting folks who want to get started with Redux, and who could benefit from learning the _patterns_ that Redux introduces, to first learn observables or generators is I think probably asking too much?

Redux-thunk is also slightly complex in the sense that redux middleware in general kind of bends your brain, but it's at least just functions/callbacks, which are easy to pick up if you are writing JS at all. I really like the idea of having a complete package of related tools to get started with, available via a single download, even if it's pitched as "learn-redux" instead of "best-practice-always-redux". Similar to how create-react-app needs tweaking as you learn all the things it set up for you, this could encourage your own tweaking, maybe show how to convert a simple redux-thunk setup to using sagas, etc. as its own form of "ejecting" ...

Just some thoughts.

I understand what you're saying, but here's my flip-side concern: we're seeing more and more anecdotal evidence that a huge group of JS devs aren't even using arrow functions and other baseline ES(6|2015) features yet, due to lack of understanding, intimidation, etc. Expecting folks who want to get started with Redux, and who could benefit from learning the patterns that Redux introduces, to first learn observables or generators is I think probably asking too much?

Bingo! We're in an environment where a LOT of people are new to JS (or they "know" JS, but it's not their specialty, and they start from the deep end). Especially if those folks are experienced software engineers from another ecosystem (java, rails, etc), they will quickly try and apply the concepts they know before learning the ones they don't, and it won't quite work and they get stuck. I don't know what's the best way to convince folks to get a deep understanding of JS before jumping in a functional UX design pattern though.

Note that the Redux docs do have a section about Reducing Boilerplate for those of you reading who are looking for something now and don't want to adopt an entire library on top of Redux.

We should be a little careful about this discussion and realize that this has probably been bikeshedded a lot already. The Redux team has heard, considered and rejected a lot of things that will probably get proposed here. It's quite possible that nothing will come out of this discussion if we only fight about things that have already been discussed.

That being said, I think it's a great idea to talk about ways to make the framework more accessible to everyone (new and experienced).

Anything you propose to reduce boilerplate should be such that it is possible to go back to the stuff below the abstraction whenever needed. It is not fun to adopt an abstraction just to drop it later to go back to the lower level stuff because you needed one extra thing that the authors of the abstraction didn't think of.

Redux learning curve can be steep, but once you grasp concepts, the need for
abstraction layers often goes away

I'm wondering, if this is the case, what parts of Redux are we suggesting to improve? What parts would you remove or abstract over that you wouldn't need to immediately add back in?

This is a diagram of the entire react/redux lifecycle I made for a talk at work about a year ago:
React Redux Lifecycle

There are quite a few parts of this, but I can't imagine Redux working as well without any one of them. Containers are kind of annoying at times, but if you remove them, you deeply couple your view logic with your data layout. If you remove actions, you're basically MVC again which is missing the point of using Redux in the first place. The "view" in that diagram already barely exists because it can be modeled as just a function that subscribes to the store and renders react components.

I don't think there is a lot of boilerplate to remove in the overall framework itself. Maybe you're referring to boilerplate in the individual parts of the framework like in action creators, reducers, or containers. In that case, the tips from the Reducing Boilerplate page mentioned above address most of those things already. We don't need anything on top of that to make it any "better". (Note: It's not that I'm not open to adding something to make things better, I just don't see it yet.)

Maybe this isn't so much a reducing boilerplate problem but a problem of improving Redux education. If the framework is hard to grok for beginners, we may need to improve the Redux docs and make it more accessible. Maybe some of the reducing boilerplate tips need to be advertised more aggressively in the docs.

Reducing the amount of steps required (boilerplate) does not always solve the problem. To stress my point from the very beginning of my post, you don't want to write an abstraction that will get thrown away because you didn't think of every way people would need to use it.

Some good discussion so far. Lemme toss out a few quick examples of common "boilerplate"-related complaints that I see:

  • "Why do I have to write 'action creator' functions just to return an object immediately?"
  • "I have to touch SOOO many files just to add a simple new feature! Also, I keep repeating the same names over and over for constants and function names and..."
  • "Why do I need these 'middleware' things just to make an AJAX call?"
  • "Why do I have to use switch statements for everything?"
  • "Why do I need this dispatch thing? Why do I have to wrap up all these functions to make them work?"

And some specific examples of these types of comments:

And to immediately toss out some responses to those "boilerplate" concerns: of those five categories listed, only "use of dispatch" is actually _required_. For the rest:

  • You don't _have_ to use action creators, but it's a good practice for consistency (per my post Idiomatic Redux: Why use action creators? )
  • You don't _have_ to have completely separate files for action creators, action constants, and reducers. The "ducks" pattern is a popular approach for putting all of those in one file together. That does have the downside of "hiding" the ability to have multiple reducers listen to one action. Redux ultimately doesn't care what your file structure is.
  • You _can_ do async work outside of Redux completely, like in a component. But, per Dan's description at Why do we need middleware for async flow in Redux?, it's usually good to extract that logic outside of components, and middleware provide a "loophole" for doing async work while having access to the Redux store.
  • You definitely _don't_ have to use switch statements in reducers, they're just the most "obvious" way of handling multiple values for a given variable. Lookup tables, if/else statements, and anything else you want are just fine (per FAQ: Do I have to use switch statements? )
  • You _do_ need to actually call store.dispatch() to make anything useful happen - that's a basic design decision of Redux. Binding up action creators makes it possible to pass them around to unconnected child components and still have them dispatch whenever the function is called.

So overall, there's almost nothing out of these "boilerplate" concerns that's _required_. It's a combination of examples from the docs and "good programming practices" like de-duplicating code and separation of concerns.

I think the questions brought up by @markerikson are really fair and I have asked them myself at some point in the past as well.

Redux is in a sense a "low-level" library for data modelling. Like any such low-level library, it exposes a lot of things that you could easily abstract over in order to account for the majority of cases. I think the reason @gaearon didn't originally do that is because he wanted to keep the library as small and flexible as possible. Thanks to that decision, we're able to build a lot of different things on top of Redux without needing to have everything in the Redux core.

Maybe we should consider that the right path might be to develop and stabilize a good library on top of Redux (like Jumpstate?). We start by teaching people that, and then give them an easy path to use Redux directly when they need to.

I don't think Redux needs very much more in its core codebase and I don't see any part of it that needs to be abstracted away permanently. (If you do, let me know :smile:) In my opinion, there isn't a lot to gain by adding or removing things from Redux core.

Improving a library to the point where it's stable and flexible enough for everyone to use is probably a better option. Like you said, not much of the "boilerplate" is actually required, so let's get rid of it in a library on top of Redux instead of modifying Redux itself.

An example of this happening in another community is in the Rust programming language. There is a non-blocking IO library for the Rust programming language called "mio". It focuses on being small and low-level just like Redux does. The thing is, pretty much no one uses it directly because that would be really hard and full of boilerplate. Most everyone uses another library called tokio which builds on mio and makes it extremely usable and ergonomic. This way, anyone who needs the plumbing from mio can use that directly, but anyone who just wants to make something quickly can use tokio.

We should adopt a similar model.

To expand on a couple of @sunjay 's comments:

There was a recent comment in #775 that I think captures things well:

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.

So yes, Redux is "just a pattern" in a lot of ways. The core library really is feature-complete - the only real semi-planned changes are things like the proposed enhancer restructuring ( #1702, #2214 ), and possibly making combineReducers more flexible ( #1768 , #1792 , etc).

Almost two years have passed since Redux was created, and we now have a pretty good idea how people are using it. As one example, @jimbolla collected a list of all known store enhancers in #2214 , and categorized how they work and what they're used for. I'm still absolutely a huge fan of Redux's simplicity and flexibility, but I'd love to see some still-idiomatic abstractions on top of Redux that would simplify general usage and solve problems for people.

One other set of semi-related topics, and something that I have a distinct personal interest in, are the ideas of "encapsulated logic/components" (per @slorber 's "scalable frontend with Elm/Redux" playground ), and "plug-and-play Redux setup" (as seen in experiments like https://github.com/brianneisler/duxtape/issues/1 , https://github.com/jcoreio/redux-features/issues/7 , etc). Global state and app setup is great for some use cases, but not so much for others.

As an interesting related point, @toranb has done some great work creating an Ember wrapper for Redux at https://github.com/ember-redux/ember-redux . The Ember world is _really_ into "convention over configuration", and so ember-redux sets up a reasonable default store config, including redux-thunk and such. Some kind of approach like that might be helpful.

So yes, I'm sorta throwing out a whole bunch of different thoughts here, but they are related in various ways. Overall, I want to lower the barriers for learning and using Redux, and enable more advanced use cases across the board.

Redux is a general API, not specialized. This makes it more verbose, while also covering more cases. Its power is in complex software, whereas any new or simple project this is beyond the scope of necessity.

However you can build specializations on top of general APIs. For instance, I use https://github.com/acdlite/redux-actions which reduces boilerplate for common cases. I still use the full power of redux and simply having that interface would not be enough for my needs.

The other problem is with new people who haven't experienced the pain of incredibly complicated applications wondering what's the point. Well, to them they probably shouldn't even use redux until they have experienced that pain, but Dan's egghead series can kick them past this point pretty easily.

https://egghead.io/courses/getting-started-with-redux
https://egghead.io/series/building-react-applications-with-idiomatic-redux

Reference: The spectrum of abstraction: https://www.youtube.com/watch?v=mVVNJKv9esE

As mentioned - redux-actions is quite good at removing some boilerplate, removing constants out of equation by attaching .toString is a really neat idea I like, however I prefer to write this logic myself than to use redux-actions myself, but thats personal choice for keeping my code smaller.

Imho there should be a built-in mechanism (subscribe's callback argument?) for observing dispatched actions stream (i know it can be done in a middleware, but thats quite restricting use case, as it cannot be used outside, i.e. by components or connect)

I often times teach redux to others and it can be quite daunting for the following reasons

  • Many moving parts: store, state, reducers, action creators, actions, async actions, middleware, connected components
  • Libraries. For a simple app that talks to a REST API you need: redux, react-redux, redux-thunk (or others) + additional libraries if you want to test things
  • Amount of places you have to put code in (see _moving parts_)

I think redux is a great library that makes total sense in itself and I think it has great abstractions and is super scalable when used correctly. I also appreciate that redux can be used with any UI library but think that in the majority of cases it will be used with react.

Generally I believe a lot of frustration comes from the many moving parts.

Here are some thoughts that I had. All of which can be built on top of redux so it can stay as it is.

  • Provide a library like react redux which additionally comes with support for async actions built in.
  • A lot of async actions are talking to a REST API so provide an idiomatic middleware for that
  • Write smart components as actual components instead of using connect. They can then render dumb components in their render method
  • Prescribe a scalable file layout for _container components_, _smart components_, _dumb components_, _action creators_ and _reducers_.
  • Provide a _redux-cli_ to generate code e.g. redux action loadUsers
  • Standardise on an async layer like redux-saga or redux-observable and include it in the docs (It would be nice to have _one_ place to consult for most redux matters)
  • default to extended language features (e.g. decorators (@connect)) or TypeScript to make things less verbose in particular mapStateToProps and mapDispatchToProps (mobx requires decorators and syntax is straightforward)

T

Agreed, redux-actions is a good example of some simple abstractions that remove boilerplate.

Let me ask this: Create-React-App has the react-scripts package, which has prebuilt Babel and Webpack configs, and comes with a bunch of sensible defaults built-in. What might a corresponding redux-scripts package look like?

I agree with some of the other (negative) comments about redux-thunk. I wouldn't recommend it to beginners (I probably wouldn't recommend it at all anymore):

  1. It seems like magic to start.
  2. It doesn't really take away that much boiler-plate. Given a more complex async process, having to get dispatch from props and pass it in works out to be only a small part of the code.
  3. It results in action creators that you cannot compose together into larger workflows.
  4. A last point that I can't articulate very well about it making the redux style into more of a framework than a library: you rely on the framework to call your code, rather than you calling into the library.

We used it pretty heavily in a React-Native application, and it was mostly useful, but we had trouble composing actions together (think "load profile after successful login"), due to redux-thunk action creators effectively returning void (ie. having nothing such as a promise to use for sequencing the next action).

It tends to encourage people to think that "the only way to do anything is to dispatch immediately from the React event handler/callback". And since we were using connect from react-redux and leaning heavily on mapDispatchToProps, we tended to forget that our event handlers could just be plain functions.

(note that the application above was mostly written early last year, and we didn't really keep up with what was happening in the greater Redux ecosystem, so it may be out of date).

@bodhi : without going off on _too_ much of a tangent, I'd disagree with some of those conclusions. redux-thunk is 11 lines long, and there's some nice explanations out there like "What is a Thunk?". Thunks _are_ composable to _some_ extent, and I think it's generally easier to tell someone "write these couple lines of function declarations, then make your AJAX calls and such here". I have a post that discusses some of the various tradeoffs of thunks (and to some extent sagas) at Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability.

I do agree that there would need to be _some_ "blessed" or "built-in" way of handling side effects in some hypothetical abstraction layer. Sagas and observables are both great, _if_ you understand how to use them. I just don't know if they'd be appropriate, given that a potential goal here is to provide an ease-in path to learning and using Redux.

I find that using redux-thunk is not just about doing something asynchronously, but also doing something that depends on the current state. Very clean, simple interface, and encourages the use of action creators.

Bodhi: Perhaps a bit off-topic, but just thought perhaps it's worth mentioning that dispatch doesn't return void, it returns the result of your inner function. So if you for instance return a promise, you can easily chain actions together. See section on composition in the readme:
https://github.com/gaearon/redux-thunk/blob/master/README.md

Other than that I would just like to say thanks to the community for having this open discussion, very interesting to hear everyone's thoughts and ideas.
Personally sticking to thunks so far but trying to clearly separate "what happened" from "how should the state change". I see quite a few instances in our code where we make that mistake (using actions to tell the reducers what to do, instead of reducers just reacting to what happened), so it would be nice to see some library that somehow made that harder to do (not sold on sagas yet).

On 19 Mar 2017, at 23:48, Bodhi notifications@github.com wrote:

I agree with some of the other (negative) comments about redux-thunk. I wouldn't recommend it to beginners (I probably wouldn't recommend it at all anymore):

It seems like magic to start.
It doesn't really take away that much boiler-plate. Given a more complex async process, having to get dispatch from props and pass it in works out to be only a small part of the code.
It results in action creators that you cannot compose together into larger workflows.
A last point that I can't articulate very well about it making the redux style into more of a framework than a library: you rely on the framework to call your code, rather than you calling into the library.
We used it pretty heavily in a React-Native application, and it was mostly useful, but we had trouble composing actions together (think "load profile after successful login"), due to redux-thunk action creators effectively returning void (ie. having nothing such as a promise to use for sequencing the next action).

It tends to encourage people to think that "the only way to do anything is to dispatch immediately from the React event handler/callback". And since we were using connect from react-redux and leaning heavily on mapDispatchToProps, we tended to forget that our event handlers could just be plain functions.

(note that the application above was mostly written early last year, and we didn't really keep up with what was happening in the greater Redux ecosystem, so it may be out of date).


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

(sorry to continue the tangent, but...)

it's worth mentioning that dispatch doesn't return void, it returns the result of your inner function.

Huh, great! Guess I just never looked too closely at the documentation before trying to find another way to solve our problems.

The one most valuable thing that made Redux so much easier for me is treating actions as events and not commands. This completely eliminates the struggle to decide how to design actions and what to put in them.

Components don't have to know how user interaction should affect the state, they only tell the outside world what did happen inside them, e.g. "button X clicked". When using actions as commands, you effectively introduce a two-way binding between component and state.

Reducers are not bound to components, they represent application state and not component state, and they are the only party that actually knows how the state changes. There's no cognitive barrier in handling single action in multiple reducers because an action is not a statement "what to do with the state".

This also removes any case for dispatching multiple actions synchronously.

Any case for dispatching single action from multiple places also goes away. You can keep action creators near the component which makes it easy to track down the component that dispatched action.

By looking at action log in DevTools you can see a complete history of what happened inside your app and it's easy to see the concrete state slice affected by the action. The history remains the shortest possible.

This pull-based approach makes the data flow truly one-way and stacks naturally with side-effect management libraries like redux-saga or redux-observable.

@aikoven although I totally agree with this (there was definitely a lot of discussion in the past comparing redux to event sourcing), what do you do with things like FETCH_USER, which is very common to see (and something that needs to be done). That seems more "command-like" then "event-like".

@blocka There's always some event that results in a network request. The request itself can be wrapped in FETCH_USER_STARTED and FETCH_USER_DONE / FETCH_USER_FAILED actions, which is how request result ends up in the state.

👍 for redux-actions

What I also like about it apart from just reducing boilerplate is, it just feels like a very natural extension of redux. For something like jumpstate I feel the implementation is going a bit farther away, and now I'll have to kind of learn two things instead of one.

So my point is, any plugins we add to reduce boiler plate should feel very much like working with redux albeit one with a less verbose API.

A lot of the boilerplate is the same boilerplate that you have in any environment. When you come across duplication, we usually solve it with a custom abstraction. These abstractions are, very often, not useful outside of your current project, and a "silver bullet" just can't exist. Every project's requirements are different.

Same thing with redux. If you fine that you need a FETCH_X_STARTED action, and a fetchX() action creator, and reducer to handle that, etc., feel free to make your own abstraction up!

Blaiming redux for boilerplate is like copying and pasting all your code, and then blaming javascript for having too much boilerplate.

I find that using redux-thunk is not just about doing something asynchronously, but also doing something that depends on the current state. Very clean, simple interface, and encourages the use of action creators.

Using redux-thunk for synchronous code isn't a great idea and should be mentioned in docs.
If you compose many functions together you aren't certain that dispatch flow is not order dependent so it's harder to reason and test.

It seems better to me to have pure actions creators and simply use actions batching for more complex state modifications. This way you have only pure functions to test.

I recommend this blog post to get better understanding: http://blog.isquaredsoftware.com/2017/01/idiomatic-redux-thoughts-on-thunks-sagas-abstraction-and-reusability/

@Machiaweliczny thanks for the article. I guess the question to me is more about where you end up putting your business logic (as discussed in https://medium.com/@jeffbski/where-do-i-put-my-business-logic-in-a-react-redux-application-9253ef91ce1#.3ce3hk7y0). You are not saying that your dispatch flow (thunk, epic, saga, whatever), should never have access to the current application state, are you?

@dfbaskin

Keep in mind that Redux isn't domain driven development. "Business logic" is a much fuzzy concern here, and it's more "what concept's function signature best fit the logic I'm trying to implement". Your "business logic" is spread out across selectors, reducers, action creators and "orchestrators" (do we have a better word for this yet ?) depending on what you need.

The reducer, being (state, action) => state is where "logic that depends on state" generally should be. And sure enough, in things like the Elm Architecture, all such logic is in the "update" function (the equivalent to our reducer).

Unfortunately in Redux, the reducer can only ever handle synchronous logic. So asynchronous "business logic" that depends on state either needs to get it from the component via dispatch (if the component has that info, which is not always), or through your orchestrator concept of your choice (thunk, sagas, epic), which have access to state for that.

If it wasn't for the reducer's limitation, there would be no need for it. You actually see it when people use redux-saga or redux-observable: their actions/action creators usually become near-trivial (in most cases, plus or minus some normalization or formatting), and the saga/epic are almost "alternate reducers", on that you now have another thing that has access to action and state and return a "thing", and the actual reducer's complexity is diminished as a result. Reducers and orchestrators are very closely related (sometimes too closely, and it's not great to have 2 constructs that are interchangeable half of the time...but it's what we have, might as well enjoy it)

@Machiaweliczny : as author of that "Thoughts on Thunks" post, I can assure you that "avoid using thunks for synchronous work" is the _opposite_ of what I was trying to say :)

More good discussion, but we're starting to drift a bit far off topic. (Is that a bikeshed I see over there? :) )

Let me point back to the original questions:

  • What would idiomatic Redux usage with "less boilerplate" look like? How can we provide solutions to those complaints?
  • What possible abstractions could be created that simplify the process of learning and usage, but without actually hiding Redux (and would hopefully provide a migration/learning path to "base" Redux)?
  • How much of this could be solved with improved docs in some way?

What concrete steps can we implement to address these?

In the realm of uncontroversial, actionable, short term steps, I would say:

1) Make the store easier to configure (maybe with a default configuration? I don't know if this is still the case, but Elm used to have a "starter app" type, and a regular app type. The starter app was used for simple apps and in training material to cover the 90% case when people are just learning. To this day, I'll admit I cannot configure the Redux store with basic middlewares (devtools, thunks) without looking up the doc for the signatures. I don't think many people would argue that newcomers need to know how to configure a store's middlewares to learn the Redux core concepts.

2) This may already be the case, make sure adding Redux with the above "starter store" to create-react-app is as easy as possible, so a newcomer can have an up and running Redux app in 2 minutes.

I think this would get us very, very far.

Edit: by starter app, i don't mean an app boilerplate, but rather a "createStore" that can't be configured but has reasonable defaults that people can just import and be done with it. Then they can just move on to the "real" createStore when they outgrow it.

That sounds like a pretty fantastic concept right there.

On Mon, Mar 20, 2017 at 10:02 AM Francois Ward notifications@github.com
wrote:

In the realm of uncontroversial, actionable, short term steps, I would say:

1.

Make the store easier to configure (maybe with a default
configuration? I don't know if this is still the case, but Elm used to have
a "starter app" type, and a regular app type. The starter app was used for
simple apps and in training material to cover the 90% case when people are
just learning. To this day, I'll admit I cannot configure the Redux store
with basic middlewares (devtools, thunks) without looking up the doc for
the signatures. I don't think many people would argue that newcomers need
to know how to configure a store's middlewares to learn the Redux core
concepts.
2.

This may already be the case, make sure adding Redux with the above
"starter store" to create-react-app is as easy as possible, so a newcomer
can have an up and running Redux app in 2 minutes.

I think this would get us very, very far.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/reactjs/redux/issues/2295#issuecomment-287806663, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AFUmCQYkUwjkCugevDBWC8TOu0HhWJTnks5rnqMWgaJpZM4MhnVF
.

Our modules make use of combineReducers so that each state slice in a module has a dedicated reducer for it (I believe this approach is outlined in the Structuring Reducers section of the docs a.k.a. "slice reducers"). This makes things easier to reason about because the switch statements are much smaller. It also allowed common reducers to emerge across our code base. The simplest reducers were identical across modules except for their action types, so we were able to reduce boilerplate by having helper functions (reducer factories?) that makes these reducers for us by action type:

const { makeIndicator, makePayloadAssignor } from '../shared/reducers';

const searchModule = combineReducers({
  searching: makeIndicator(c.SEARCH),
  results: makePayloadAssignor(c.SEARCH_RESPONSE, [])
});

Perhaps identifying more common slice reducers like this would be a good way to alleviate the boilerplate concerns, where they could serve as primitive building blocks for our reducers.

This is basic javascript refactoring. As I said before..."javascript" has a
lot of "boilerplate", until you apply things like abstraction to your code.

Redux is just javascript. There is nothing magical about it.

On Mon, Mar 20, 2017 at 12:44 PM, Markus Coetzee notifications@github.com
wrote:

Our modules make use of combineReducers so that each state slice in a
module has a dedicated reducer for it (I believe this approach is outlined
in the Structuring Reducers section of the docs a.k.a. "slice
reducers"). This makes things easier to reason about because the switch
statements are much smaller. It also allowed common reducers to emerge
across our code base. The simplest reducers were identical across modules
except for their action types, so we were able to reduce boilerplate by
having helper functions (reducer factories?) that makes these reducers for
us by action type:

const { makeIndicator, makePayloadAssignor } from '../shared/reducers';
const searchModule = combineReducers({
searching: makeIndicator(c.SEARCH),
results: makePayloadAssignor(c.SEARCH_RESPONSE, [])
});

Perhaps identifying more common slice reducers like this would be a good
way to alleviate the boilerplate concerns, where they could serve as
primitive building blocks for our reducers.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/reactjs/redux/issues/2295#issuecomment-287820595, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AACourLpL5--NJiRzrWmKFJ31cl5DXJrks5rnqzxgaJpZM4MhnVF
.

I agree with most commenters that said that is better to explain common redux patterns than develop some boilerplate reducer. My teammates and I started to work with redux 1.5 years ago and were mostly confused about simplest things: that redux is mostly event-sourcing, that the same action can be handled by several reducers, that action can contain whole business enitity not only its id, that reducers are just functions and you are free to do anything you want with them - compose, generate, split, etc. We made common mistakes - used actions as remote commands and wondered why just not to use classic method calling; created huge middlewares and avoided redux-saga because "generators is too complicated" (though redux-saga is superb!), made long files with huge switches. This is of course because of mediocre programming skills but also because of lack of structured information. Today documention is much better, thanks a lot to the maintainers!

This is a very valuable topic on Redux as a library.

This is basic javascript refactoring. As I said before..."javascript" has a
lot of "boilerplate", until you apply things like abstraction to your code.
Redux is just javascript. There is nothing magical about it.

Perfect, @blocka! I think this message needs to be spread.

Redux is feeling unusual and boilerplate-ish to people because of its minimal approach. Minimal feels weird for a "framework" in general (minimal code, minimal principles).

Some people are used to writing "framework-y" code rather than writing JavaScript itself. When developers use a framework, built-in way do to things are implicitly expected. Sad as hell but true.

I believe Redux is getting easier to get started with than it ever was, but we still need to make it clear for people to not to expected Redux to do everything. @gaearon clearly says this all the time, but unfortunately people are used to frameworks doing everything. Why should they get motivated to learn something that "doesn't do much"? Angular does a lot more than Redux. Why Redux? The answer is probably evident for all of us in this issue. But is it clear to beginners?

I wrote a short article about it yesterday: Don't blame it on React or Redux. Of course I can be wrong on all this.

There is precedent for having abstraction layers on top of an advanced library. Just recently, Tensorflow brought in Keras as an abstraction on top of the core API: http://www.fast.ai/2017/01/03/keras/.

Using TensorFlow makes me feel like I’m not smart enough to use TensorFlow; whereas using Keras makes me feel like neural networks are easier than I realized. This is because TensorFlow’s API is verbose and confusing, and because Keras has the most thoughtfully designed, expressive API I’ve ever experienced. I was too embarrassed to publicly criticize TensorFlow after my first few frustrating interactions with it. It felt so clunky and unnatural, but surely this was my failing.

I feel like that is a similar experience for most first timers coming into Redux. Replace Tensorflow with Redux and Keras with Jumpstate. Redux is very powerful, but the majority of users probably don't need all the control that is available. More than likely, they're coming from Angular or being taught React + Redux at a bootcamp or from watching various tutorials. While Redux doesn't need to be dumbed down for new users, it also isn't an anti-pattern to provide an easier abstraction that can probably cover 80% of potential use cases.

Y'all, if redux was just fine the way it is, we wouldn't be having this conversation. Regardless of whether redux is useful or structurally sound, people still feel "bad" when they're first learning it. These feelings are valid.

The people commenting in this thread are all redux experts -- of course we're happy with redux; we have internalized its philosophy and we feel comfortable making our own abstractions for managing "boilerplate." We are not the audience this thread is intending to serve. We need to have some empathy for users who are coming to redux for the first time, maybe even coming to functional programming for the first time.

For example: here's a complaint I frequently hear about redux (that I dont think has been mentioned upthread): its not enough to just use redux, you also "need" to use react-redux, redux-thunk, redux-actions, etc.

Do you literally need to use all of those other packages to use redux? No.
Will most redux users end up using some or all of those additional packages? Yes.

Does the number of packages in package.json matter? No, not really.
Does the number of packages in package.json affect the way people feel? Absolutely.

Now, I think its fair to believe that redux itself should remain as it is, and another package (create-redux-app or whatever) should handle assembling them together. But we have a real complexity problem on our hands and its not enough to tell the users to RTFM.

Regardless of whether redux is useful or structurally sound, people still feel "bad" when they're first learning it. These feelings are valid

Absolutely. But not all people feel that way. Some do. Keep in mind that you'll never hear from people who didn't have problems. You only see the ones who ask you questions, or hit stackoverflow/reactiflux. Of those who never do, some also need help but don't know how to ask for it...and some are doing fine, too, and you don't want to make it worse for them...else they'll be the one coming to forums instead and it will be a net zero gain.

Redux didn't get popular for no reason. A heck of a lot of people thought it felt darn good and recommended it to others :)

Now, I think its fair to believe that redux itself should remain as it is, and another package (create-redux-app or whatever) should handle assembling them together.

I liked this idea. It'd be possible to argue that existing boilerplates already solve it, but React also had a lot of boilerplates at the time that people were complaining about being overwhelmed with tools when trying to get into React. create-react-app was a great answer to all this, because it has eliminated a lot of steps for beginners to get into React _itself_ already.

But it's still boilerplate under the hood. So, we still need to encourage people to buy into the ecoystem and make it clear that Redux itself doesn't do much by design. It's not possible to run away from the ecosystem if you want to use React or Redux.

I don't believe that including packages such as redux-thunk in Redux core is the way to go here though.

Just for clarity, the kind of ideas I'm tossing out aren't about actually including things directly in the core. They're about hypothetically creating some kind of "pre-built configuration" or "ease-of-use abstraction layer" that would be a separate package or CLI tool or something, more along the lines of create-react-app.

I figure I'd better chime in as the author of Jumpstate/Jumpsuit.

TL;DR:
Redux is hard to learn and hard to configure / integrate with because it is so low-level. The community could benefit from a standardized higher-level redux api aimed at the 90% use case to ease the learning curve and even operate as a solution for simpler projects. Jumpstate is trying to solve this, but is not a long term solution. We need to start proposing possible ideas (real or meta code) on how this api could look.


Redux is very difficult to learn out of the gate and, very often, just as difficult to integrate with new concepts like async logic, side-effects, custom middleware, etc. I experienced this myself only a little more than a year ago when I was very new to the react ecosystem.

Only a few days into learning react I was immediately recommended to learn redux. This recommendation came from both very experienced and novice devs who had done (or were doing) the same. It was being hailed as the de facto solution for anything that extended beyond setState (let's keep setState gate out of this though 😉 ). All of this for good reason, because redux is amazing.

I inevitably hit the learning curve and when seeking help immediately realized that the marketing and documented approach for redux took on a tone of "Redux is hard, the concepts are low-level, the api is locked in, and you just need to power through it."

So this is what I did along with all of my peers. We powered through it because it was what we were educated to do. Along this journey though, I dared to create an abstraction layer very early on that would temporarily and deliberately hide some of the complexity and advanced features that redux had to offer.

I didn't need custom action creators, didn't want to worry about mapping actions and dispatchers to components, and like many others, opted for a less verbose reducer layout in place of a switch statement.

It worked fantastically. So well, in fact, that it expedited the process of learning redux for the rest of my peers and other devs who were similarly struggling with the learning curve.

We designed it in such a way that advanced or "idiomatic" redux could still be used when we needed it, but truly designed it for our 90% use case.

By no means am I saying that jumpstate has covers all the bases, in fact, there are still sooo many things that need to change for jumpstate to behave "idiomatically" (customizable action creators, removing global action imports... there's a lot), but it has definitely helped many people learn the basics and gave them a path to understand how redux works on a very simple level. All the time I receive message in the Jumpstate slack org that jumpstate helped someone learn redux.

Whether those people are still using jumpstate today or not doesn't matter that much (but many people are still using it production today). What matters was the experience they had, and how quickly they were able to become productive in the redux ecosystem.

Some more ideas that come to mind for this discussion:

  • propose meta examples of how a simplified redux might look, just take a stab at it!
  • discuss requirements for, and decide on a standard for the often duplicated and extremely opinionated redux middlewars. We could start with async handling. This could solve so much fragmentation in the redux community and help everyones efforts make a difference to the long term survival and usage of redux.

How much of this could be solved with improved docs in some way?

A lot. When it comes to the Redux documentation, I feel to many degrees it is worse than AngularJS 1's docs because like AngularJS's, it is unnecessarily verbose but unlike AngularJS's, it fails the "show, don't tell" principle.

Redux's docs prefer code snippets rather than actual demonstration of the code which means there's too much "here's a set of code; we won't tell you how it connects but trust us that it works" moments. The lack of ability to run examples within a browser means that the user is forced to trust their own instincts that what they are doing locally works and if something doesn't work, they are off on their own. Even the simple Todo List app I feel it's hardly the best "hello world" example of Redux - it could be made much simpler.

Diagrams would certainly help here. For example, I like @sunjay's one; one that might even be better is to design one for the the Todo List example itself - how are the files connected, etc. from a high-level view. Images are almost always better for conveying long text and can help reduce the verbosity of the docs. The images would also help beginners keep their focus and better understand Redux as a whole.

My two cents: please don't change core. Its brilliance is in its simplicity. At this point, adding something to it would likely take away from the whole.

The user-friendliness and further abstraction can happen in userland. I don't feel like there needs to be any official or idiomatic way to use it either. It's a predictable state container, that's it!

It can stand on its own, let's​ it be ❤️

That said, sure, package it up however you want by creating more opinonated libs on top of it. That's what I do too, but let's not change core, pretty please.

@HenrikJoreteg, have no fear! I'm almost certain that nothing we discuss here is ever going to change the core. The core is feature complete and stands on its own. :)

A lot. When it comes to the Redux documentation, I feel to many degrees it is worse than AngularJS 1's docs because like AngularJS's, it is unnecessarily verbose but unlike AngularJS's, it fails the "show, don't tell" principle.

This is very true. One thing when Redux was new is that doc was sparse, but the basics were there. Many people adopted it because it was "so simple". But online resources are now trying to show "real world techniques", which often are just "my favorite middleware" and "my favorite boilerplate reduction tooling". People get overwhelmed.

There's something to be said about "less is more"

@Phoenixmatrix , @Sylphony : so, what would a potential docs revamp look like?

Personally, to combine it with my suggestions above of having a "starter-store", I'd split it in two (not 2 different sites/documents, but 2 different sections).

The "Redux architecture", which would have a lot of cute, simple drawings, explains actions and reducers, introduces react-redux and maybe thunks, uses the "starter store" to limit any kind of upfront configuration, and shows how easy it can be to make a simple app that does a few ajax requests and render stuff on screen.

Everything else can go in an "explore" section with the more advanced concepts. Even then, I'd probably rip out a lot of stuff. Someone pointed out to me today that there's a section on having multiple stores in an app. That's good to know, but the uses for it are really advanced. I don't think any newcomer needs to stumble on it without looking.

Better docs are always a great idea, but I don't think that solves the core issue here. Abstraction libraries are being written and adopted because of the boilerplate and verbosity of the Redux api. Those same libraries are fragmenting the Redux community because they are obviously needed at some level or another. It is frustrating when those same authors who are actively trying to contribute to the ecosystem are put down and dismissed publicly because they "don't understand Redux". If the community can instead create a simpler abstraction, not part of React core, but "blessed" to some degree, then everybody wins. Hardcore users keep using Redux as is, other people can use an abstraction with an easier/less verbose api.

@derekperkins How do we come to consensus about a "blessed" library? How do we decide what goes in it and who is going to work on it?

I'm all for something like this, but how do we avoid just creating another library that only some people use and thus fragmenting the ecosystem even further.

Relevant xkcd: https://xkcd.com/927/

Yeah, redux's core and react-redux as pretty much as close to a consensus as one can get. Even redux-thunk and redux-actions are pretty borderline, because even at that level the majority of people have different needs.

Reducing boilerplate in Redux is a matter of "I don't need so I'll abstract it away. But even for newcomer, "that part" varies dramatically.

Heck, we have a lot (several dozens) redux apps at work and it's hard to build a common boilerplate reducing stack because different apps have different requirements. It's easy when you have a single app, or a few with the same requirements. Even small ones differ quite a bit.

The idea here is not to put together a toolkit that solves all problems for all people. Using CRA as an example: there's tons of ways to configure Babel and Webpack and ESLint. What CRA does is offer a single, opinionated way to get them all working without any hassle on the user's part, and it deliberately picks out a number of settings that make things better for learners. It then allows the user to take control of configuration _if they want to_.

It also doesn't try to solve every single use case. CRA doesn't try to tackle advanced code splitting, multiple entry points, SSR, or stuff like that. The CRA docs _do_ point to other tools and options that may offer more configurability and work better for certain use cases.

That's a great way to put it. Something to get up and running fast that
only involves installing redux and this hypothetical config layer? That
would be awesome.

Another idea I had would be a flag you could set in dev mode that could
watch for and expose incremental learning opportunities for the user. In
this way the abstraction layer could serve as an interactive teaching tool.
On Mon, Mar 20, 2017 at 4:14 PM Mark Erikson notifications@github.com
wrote:

The idea here is not to put together a toolkit that solves all problems
for all people. Using CRA as an example: there's tons of ways to configure
Babel and Webpack and ESLint. What CRA does is offer a single, opinionated
way to get them all working without any hassle on the user's part, and it
deliberately picks out a number of settings that make things better for
learners. It then allows the user to take control of configuration if
they want to
.

It also doesn't try to solve every single use case. CRA doesn't try to
tackle advanced code splitting, multiple entry points, SSR, or stuff like
that. The CRA docs do point to other tools and options that may offer
more configurability and work better for certain use cases.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/reactjs/redux/issues/2295#issuecomment-287914805, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AFUmCactFzsw7etmGGP8MFK6kfNaOzTJks5rnvorgaJpZM4MhnVF
.

@sunjay I knew what xkcd link that was without even having to click. :)

I'm in agreement with @markerikson. This isn't an attempt to handle every edge case for every person. It's an opinionated statement from core Redux devs saying, "this is a good way to get things working". There are sane defaults that can be overridden if needed, but I would wager that the defaults are going to work for 80% of devs.

That may or may not be written on core Redux. I still think there's a place for something like Jumpstate that does more than accelerate the learning curve.

Redux learning curve can be steep, but once you grasp concepts, the need for abstraction layers often goes away

I think that's true to a certain extent, but doesn't necessarily mean that you always want to eliminate the abstraction layer. Even though I have written C in the past and understand memory allocation, I much prefer to write in Go where garbage collection is handled for me. Per my Tensorflow example earlier, it'd be more performant to write to the lower level apis, but there's also a lot to be said for the ease of use of the Python keras abstraction. Similarly, for Redux vs Jumpstate (or whatever abstraction comes from this issue), it isn't that boilerplate isn't necessarily hard to write, it's just cumbersome for most use cases that don't need all the flexibility of raw Redux.

If it were up to me I’d like to see this:

  • A Flow/TypeScript friendly subset that is easier to type than vanilla Redux.
  • Not emphasizing constants that much (just make using string literals safer).
  • Maintaining independence from React or other view libraries but making it easier to use existing bindings (e.g. providing mapStateToProps implementations).
  • Preserving Redux principles (serializable actions, time travel, and hot reloading should work, action log should make sense).
  • Supporting code splitting in a more straightforward way out of the box.
  • Encouraging colocation of reducers with selectors, and making them less awkward to write together (think reducer-selector bundles that are easy to write and compose).
  • Instead of colocating action creators with reducers, getting rid of action creators completely, and make many-to-many mapping of actions-to-reducers natural (rather than shy away from it like most libraries do).
  • Making sensible performance defaults so memoization via Reselect “just works” for common use cases without users writing that code.
  • Containing built-in helpers for indexing, normalization, collections, and optimistic updates.
  • Having built-in testable async flow support.

On top of core, of course, although it’s fine to brand it in as an official Redux thing.
Also, I won’t be writing this. But you can.

For me,few month ago when I learn redux,I read the doc but don't understand very well,So I spend 2 days to read the src code,then every description about api of the doc become clear.

And then,the thought about the "state design","reducer division","middleware design","async solution"...... chapters become clear too with I develop the project next months.

So,I think read the src code is the fitst step to learn redux,and only after that,you can understand and explore the redux's environment more smooth.

async support by default.

I mean no need to use another plugin such as redux-thunk, redux-saga.

async support by default

It does, it's just that people look at it for 0.5 seconds before looking for something better. Nothing stopping you from calling APIs inside your components and then calling dispatch in the callback/promise resolutions. It's just that people want to externalize it right away (me included). But you can do async just fine without any middleware.

@gaearon's comment is perfect, in that it highlights just how broad a sweep there is. I totally love and appreciate the precision and elegance and power of the Redux 2.0 that he proposes, and yet almost none of those improvements are relevant to me!

I actually find that fascinating.

My own Redux abstraction library does almost entirely the opposite. I find immense value in single-reducer actions, in bundling atomic reducer functions with their action codes and selectors, in killing off strings entirely in favor of object keys. It's my own 90% solution, more similar to CRA in attitude than anything else.

The interesting point is very much about: what problems are we trying to solve? There are many problems, with several different solution spaces available. If Dan built Redux 2.0 the way he described, I'd rebuild my own abstraction library on top of that too!

@jbellsey Redux is Great in providing actions and state to app globally — but in defined scope — you can have infinite instances of redux store but you can combine them. Basically this is useful for libraries. If you see a library talking about redux inside, you can benefit from...

what problems are we trying to solve?

Global state of the App. Reliable aEsthetics Delivered Unified eXtensible :heart:

No one wants globals. But everyone needs one :tm:

Please, keep redux simple as is now. It is pattern. Cookbooks will always arise around :100:

@markerikson Would be a good idea to start a document / spreadsheet to organize these thoughts? There have been some amazing ideas already and it's only been 48 hours 🎉

For me, I wouldn’t recommend people to use thunks or sagas. Instead, learn dependency injection techniques in JavaScript.

Redux gets easier once I treat it as a plain database:

  • I tell Redux the initial state, and how an action (event) affects the state of the application. (Like database schema and stored procedures.)
  • Then Redux takes care of managing it and notifying the listeners, and provides ways to inspect and debug the state.

For me that’s all Redux is to me (I wouldn’t expect a database to make an HTTP call). For I/O and asynchronous stuff, I perform it outside Redux. This keeps my Redux store predictable. The type signature is always store.dispatch(actionObject). No dispatching functions and no hidden meaning inside the action (e.g. dispatching an action will not make an API call).

@tannerlinsley: Yeah, go for it.

One downside to me starting this discussion : I've already got a ton of stuff I need to work on and write about. Much of it is self imposed, but it means I don't have enough time to work on some of the Redux-related topics I'd like to investigate, much less tackle things like a major docs revamp or build up a new toolkit from scratch.

I absolutely want to be involved in these possible efforts, but it's going to need other people to join in and work together. (Famous last words of every OSS maintainer... :))

Redux gets easier once I treat it as a plain database

This is what I recommends!

  • Keep your store normalized
  • You can prepare data — like views. Do this by dedicated reducers — you then do not need reselect. Consider.
  • Actions should not trigger another actions — this can looks easy. But it is hard if you have componentDidMount hooks. I don't know how to solve this. Shifting this up in hierarchy is not always easy

FWIW, I've been using Redux with D3.js (without React) and it's been going quite well. Some examples of this are listed in d3-component.

The thing I miss the most about the React Redux bindings is connect. The point I'd be most interested in from the above outline/vision from @gaearon is "providing mapStateToProps implementations".

You can prepare data — like views. Do this by dedicated reducers — you then do not need reselect. Consider.

Yeah, this is kinda what twitter does in its store, at least I think so - they have entities branch which contains regulars ids to data maps and i.e. timeline view which is basically the main feed you see which holds id references to the entities branch but its already precomputed.

And thats what Im going for in the upcoming refactors, as reselecting highly dynamic lists is... not too performant ;) the only downside I see is that you kinda end up with duplicated data, and also manipulating updates and deletions to the view might be quite cumbersome in the long run as u need to remember often about 2 places (or more).

@stefensuhat when I tried to implement Redux on my own I used a Promise loop to make async actions possible. You can check it out at https://github.com/iddan/delux

@gaearon totally agree with your list! We've been working on a redux boilerplate following 90% of the list at the organization I'm working (also, following the ducks principes). It's an early release at the moment, not publishable-ready.

I would definitely like to contribute to this good idea!

@markerikson @tannerlinsley

As for organizing these ideas, I suggest that after you make the spreadsheet you create Github issues in the appropriate repos to track the development. You should then comment here with a list of those issues.

This doesn't guarantee that anything in particular will get shipped with redux, but it does give people interested in that particular thing an opportunity to follow the discussion and work together to make it happen.

The burden of making these suggestions into reality is on all of us that are interested in each new feature or approach. Though Mark started the discussion I'm sure no one is expecting him to go and implement it all. :)

Yeah, @gaearon's list is ideal, but as some people mentioned already, that is a lot of stuff. Anyone attempting to tackle it straight up will face bikeshedding to oblivion. I'd love to see it happen regardless, but I think smaller pieces should be attacked at a time, rather than building a full layer on top (again pushing my example of a preconfigured starter store ;) )

With that said, when/if someone does try to attack that whole list, also keep in mind what other tools and frameworks exist outside of Redux, and even outside of React. React/Redux has an interesting sets of benefits over vanilla Flux, over MobX, over VueJS and it's ecosystem, over Angular. Whatever the community does, it probably wants to keep focus on some of these benefits (some of which are part of @gaearon 's list), and make sure that whatever layer is built on top doesn't become totally redundant. A lot of things like time travel debugging, middleware ecosystems, etc are available elsewhere.

Example I'm making up: it can be tempting make a module that let's you make a function that automatically updates an automatically generated reducer. But once you do that, you replicated MobX, so that's not very useful to do.

@Phoenixmatrix yes what I mean is redux has default middleware , so we don't need to use external one. 😄

I don't really see anything wrong with current implementation of Redux. Boilerplate exists but it's really not a big deal if you have a type system.

One major problem to me is that people don't seem to understand that we are building frontend applications that are way more complex than they used to be some years ago. If we are building applications that are bigger, more complex, with large teams, it's normal to me that the difficulty increase significantly, that new patterns emerge, yet people reject that difficulty.

I'm not saying that we shouldn't try to make things simpler, or reduce boilerplate, but that people should not start to use Redux and complain about the boilerplate involved without understanding the tradeoffs being made and seeing the big picture.

When Redux was created, a lot of people (me included) where trying to build that thing, and @gaearon got the best implementation and did a very good work on tooling/demo. It (and also Flux) got a lot of traction initially, not only because of the time-travel, but also because the concept where not new for many people and immediately feel natural.

People see Redux as a new shiny toy (that seems to end up being not so easy in practice). Yet nothing in Redux is really new except the fact that someone did port those concepts in an elegant way to frontend. The patterns of Redux and its ecosystem are already used in distributed systems, event-sourced backend applications, domain-driven-design, and people having background in these will understand Redux very easily.

Unfortunately, people want to skip steps and use Redux without knowing anything about the more-general patterns involved. Personally I'd recommend anyone wanting to build something serious in Redux to have a minimal background in distributed systems. That may seem weird but I think having that kind of background can give you a very serious advantage right now, and in the years to come.

What are we building exactly when we build a frontend application?

  • We build a member of a distributed system
  • That member only holds a subset of the data of the more general system
  • That member has quite limited capacities (storage, bandwidth, network stability...)

So, we try to build something very complex. We have constraints that are more difficult to handle than database developers, yet we want it to be simpler out of the box, without reusing previous knowledge of people that really thought hard about these problems during lots of years. Don't you think that people already thought about how to structure the state inside a DB? how to query it?

I'm not saying we should not be pragmatic, shipping applications today and make mistakes with our current knowledge, but rather understand and acknowledge that what we are building is not simple anymore and requires additional knowledge. Redux might be a good path to obtain this knowledge, and you can use it in a pragmatic way at first, but don't underestimate how much frontend development will make you uncomfortable in the years to come.

Unfortunately, people want to skip steps and use Redux without knowing anything about the more-general patterns involved.

For better or worse, Redux has essentially won the battle for React state management. With that comes the ability to shape the ecosystem. There will always be devs who just want something that works, and there are plenty of simpler applications where that is all that is necessary. Either people will continue to use other abstraction layers, or they can be introduced to the correct general patterns via a simple library.

Personally I'd recommend anyone wanting to build something serious in Redux to have a minimal background in distributed systems. That may seem weird but I think having that kind of background can give you a very serious advantage right now, and in the years to come.

This is the crux of the issue. Redux as is works great for people who understand it. It isn't necessarily a bad thing however to simplify it. Most people aren't writing assembly code anymore, even though understanding it typically helps you to write better code. That's just not where most people start.

Lots more good comments all around. There's tons of ideas here we could and should explore further.

Let's see if we can start coming up with some potentially actionable ideas. I'll start, by tossing out a small baseline concept:

Create a new package called, I dunno, redux-starter or something. That package might depend on redux-actions, redux-thunk, and redux-logger, and maybe a promise-based middleware. It would provide a function with the signature function createReduxStore({reducers, middleware}) {} (based on @Phoenixmatrix 's comment earlier ). Might not even bother accepting middleware?

The createReduxStore() function would take care of handling applyMiddleware and setting up hot reloading for the root reducer, as well as configuring the Redux DevTools extension. If no middleware are provided, it would configure the included ones by default. This might look similar to the store service setup code in ember-redux or the configureStore function in my "Project Mini-Mek" sample app.

This would at least minimize the amount of setup needed to get started, and provide a modest set of useful functionality.

Yeah, i like that (well, don't like redux-actions and promise based middlewares for newer users, but I'll let you have fun with the bikeshedding on that one). Wouldn't bother accepting middleware, just createStarterStore(reducer).

Maybe even replace combineReducer, so you can simply do:

const store = createStarterStore({
   foo,
   bar,
   baz
});

where foo bar baz are reducers.

Drat. Just realized that "a library function that sets up HMR for the root reducer" probably won't work, because even if we allowed the user to pass in the path to their root reducer file, one or both of the module.hot.accept() API and require(path) re-import step want paths that are statically analyzable at build time and not variables. (I think... I could be wrong.) Oh, well.

But yeah, createStarterStore() could accept either the root reducer or the slices object.

Personally I'd recommend anyone wanting to build something serious in Redux to have a minimal background in distributed systems. That may seem weird but I think having that kind of background can give you a very serious advantage right now, and in the years to come.

@slorber I agree with you on that part.

Unfortunately, people want to skip steps and use Redux without knowing anything about the more-general patterns involved.

But not really on this one. Some people do know about the patterns involved in Redux, and this is precisely why they chose to use it. Wanting to use something simple (or at least simpler) doesn't necessarily mean the user isn't capable of using a more difficult implementation. It just saves time and effort.

At the place where I work, we've used Redux for a year now, and have found ourselves copying/pasting the same code (only modifying it a bit) to add new resources to the store, which is why at some point we felt the need to implement a layer on top of Redux, to abstract this redundant boilerplate.
In my opinion this idea isn't just simplifying the use of Redux, but also reducing the boilerplate for the most common parts (in the same fashion react-redux reduces the boilerplate when using Redux along with React).

Very interesting discussion. My two cents as a redux user since the earliest days:

In the beginning, as I recall it, redux was viewed more as a protocol or set of reasonable conventions than a framework to build apps. This austerity is part of why redux can be overwhelming to new people and tedious to scale without rolling your own abstractions. But the simple way it cut through the mess of managing state in 2015 is also a reason it took off, and the minimalism has allowed for amazing frameworks like Apollo to piggyback on work done for different projects (especially dev tools) and give a familiar way to "drop to a lower level" in state management when need be.

I think it's clear that as a "low level" library redux has been a huge success, and as a feature-rich framework for building apps it's not quite there yet. One thing to be careful about is that as redux evolves it doesn't break it's role as an industry standard "protocol" for projects like Apollo.

To this end, perhaps current redux can become a package like redux-core, and redux itself raises the level of abstraction to give a recommended way of building apps. Of course, there can be any number of higher level alternatives to this redux (like Apollo, or something using sagas?) using redux-core...

Restating things for clarification and emphasis:

Any work out of this thread will not include major changes to the existing Redux library :) A docs revamp is totally possible, and there might be some small tweaks, but the existing redux package would stay as-is.

Anything like the redux-starter package I just described, or the "framework"-y approach Dan outlined, would be a new package that depends on redux.

I vote we create the package like @markerikson suggested. Once we have a codebase, we can start batting around api, code snippets and more in-depth ideas. We would then have a place to create issues for specific features.

You can use Delux as a starter (jk)

As a heavy user of @tannerlinsley's Jumpstate (and now one of the maintainers) I'm incredibly excited about this. My reasons for using Jumpstate are very succinctly outlined in @gaearon's original post. Saying that, it is not a complete solution (it was never intended to be). I was thinking about a few things that could be added on top of this just this morning and then I discovered this thread and found you guys are already thinking about it.

Long winded way of saying: Sounds awesome, I'm in, not sure how much I can contribute (I will try) but you certainly have a cheerleader.

I have used Jumpstate to teach Redux several times now and can confidently say that it has shortened the learning curve considerably. I think of Redux like HTTP and Jumpstate like fetch. I love Redux and have written some (ridiculously) complicated things with it, but I also know that 90% case is more than enough to teach most things.

When you think about it this is a great place to be. One of the core complaints about frontend development and javascript in general is "the lack of standard libraries." Thing is, this is a very new thing (TM), being able to develop full applications in the browser. The pressure is finally rising to need standard libraries. That's where we are now.

Re: Learning curve, opinionatedness and abstraction:

Ajax seems to be a area where new learners get stuck, whether they're learning to use thunks, sagas or epics. One area where they don't seem to have much difficulty is creating actions - as they're just plain old Javascript objects, and we have the added clarity provided by the Flux Standard Actions spec. This line of thinking led me to the question - Why not develop a Flux Standard Ajax Action?

E.g.

function fetchPosts(page) {
  return {
    type: 'FETCH_POSTS_REQUEST',
    // Ajax request setup
    ajax: {
      url: `/api/posts`,
      method: 'GET',
      data: { page },

      // Optional ajax meta attributes
      meta: {
        // Amount of time to debounce the execution of the request
        debounce: 400,
        // Amount of times to retry the request on failure
        retry: 2,
        // Amount of time before timing out
        timeout: 10000,  
        // The action type that cancels the request
        cancelType: 'CANCEL_FETCH_POSTS',
        // Strategy when multiple FETCH_POSTS_REQUESTs are in flight
        resolve: 'LATEST'
      }
    }
  };  
}

The ajax action would be handled by middleware (which would react to the ajax key in the action).

I've been trying to think of reasons why this approach would be a bad idea, but have not yet thought of any deal breakers.
Some benefits in my estimation:

  • a dramatic decrease in the amount of async actions required in your average application (as the ajax related async actions would fall away)
  • easy to understand as we're only dealing with plain old javascript objects
  • it's declarative, implementation details aren't important
  • different implementations could be swopped in/out without breakages
  • it's idiomatic Redux (action log still makes sense, serializable etc)

I can hear some people invoking the separation of concerns argument, but I would argue that it's a natural extension to what our ajax request actions are doing already. Currently they're setting up an action with a pretty clear context, 'FETCH_POSTS_REQUEST', but with too little context to abstract away the implementation of the ajax request. Leaving little choice but to manually perform the ajax request somewhere else.

Another thing that comes to mind is the async action chaining that I've seen in some docs. We've not had the need for chaining requests in this way, so I'm not sure how widely used this chaining is, but my gut feel is that it would be outside the scope of what I'm proposing here (and maybe doesn't fall within the 90% use case mentioned in this discussion).

In conclusion - it seems quite possible that an abstraction like this would help ease the learning curve and the amount of code necessary to write common Redux applications. Does anyone have any thoughts on this approach - any deal breakers you can think of? @markerikson @gaearon

Prior art: redux-axios-middleware

Lots of stuff going on in this thread.

Regarding the discussion about Redux complexity. Personally I disagree with people complains about Redux being complicated and verbose. It's a trade off you make when going with a low level library based on Flux & Elm architectures. It is stated in the Redux home page that you might not need it. But we still have people complaining about it being complicated.

Redux is find as it is. Lucky the core team is aware of it being feature-complete and not willing to change it. The abstractions built on top of it are awesome and each of them have its own use case. This is what you'd expect for a good and well designed low level library.

I see the problem not on Redux itself but on the lack of a progressive, working-out-of-the-box way to learn it. As some mentioned, React itself had the same problem and create-react-app was the solution.

Maybe that is not even the problem since there are awesome resources for that (i.e. Dan's egghead series). Maybe people wanting to use a library that solves complex problems without any learning curve is the problem.

Anyways, this is not what this thread is about.

@markerikson I am personally up to help on developing this redux-starter.

_I'd love a cooler name though_ :stuck_out_tongue:

The boilerplate required in producing many actions is pretty much the only downside I see with redux.

It is very low-level, but I think that's great. That's one reason why I prefer the Redux-style paradigm over MobX for example, though I do understand it's usefulness and would use MobX. But I'd only be happy doing that now that I understand Redux. Because Redux (or the Redux pattern in general), the data doesn't particularly go "behind-the-scenes". Generally, wherever code is being processed, you wrote it, and if not, you can understand it (if you've taken the time to learn, more on that later).

The thing is, when I was first learning about Redux, it was a lot to understand, but it was also simple. Something can be be both difficult and simple. However because of that, I thought, "Wow this concept is so logical! It makes so much sense I bet I could do it myself!" So for my first project, instead of using the Redux package, I made my own little Redux store.

Doing that not only helped me understand the concept of Redux, but increased my understanding of Javascript itself a lot! (This is from the perspective of someone who knew almost nothing about functional programming concepts before this, so your mileage may vary.) The concept of Redux is really quite brilliant in it's simplicity, yet also incredibly powerful.

I think the thing is that since Redux is so popular, it's, uhh, "buzzwordy-ness" can distract newcomers from just how great it works in Javascript. So their mentality may be "ok I got React, Redux, etc etc, now I'm going to be amazing programmer and make great app!" Not that there's anything wrong with that. But they may focus on just wanting to use a shiny new tool to expedite the development process, without understanding what makes the tool work.

Once the concept is understood, it becomes very easy to get Redux (or any Redux-like pattern) to do all manner of things. I like it for the same reason I like React over other (still great) tools like Angular. _It's just Javascript._ This is how Javascript works!
If the necessary boilerplate can be reduced through some paradigm that would be great, but I think it's a small price to pay for unlimiteddd powaaaaaa.

IMO all this "the verbosity is a tradeoff" is just masochism; the verbosity isn't in service of anything, it's a sacrifice you make to use Redux at all. I'm not blaming Redux or javascript, I just want to point out that this isn't essential complexity. Elm manages everything Redux achieves in fewer lines and simpler code.

So we mechanically write out the boilerplate. Jumpstate and similar make life easier, but there are limits to what you can do at runtime, and with typescript/flow jumpstate doesn't help much. But codegen is tough, and no one wants to add one more thing to their already overburdened build system.

If I could have something like Elm code that was translated into idiomatic Redux... Be still my beating heart.

Here are some of my ideas, hope I don't repeat previous comments:

Action creators can be used as action types

actions.js

export function increase() {}

reducer.js

import { increase } from './actions.js';
export default handleActions({
    [increase]: (state) => state + 1
}, 0);

As functions have unique references we can treat them as identifiers for actions. Not FSA compatible but saves a lot of code and prevents mistakes.

Actions can be dispatched asynchronously

async function a() {

}
async function b() {
}
store.dispatch(a());
store.dispatch(b()); // b() will be dispatched after a() resolves

That way action creators can be async functions and yet dispatch one after another. As I see it in many situations people will not need to use middleware based solutions and it will save a lot of boilerplate code.
This concept is implemented by a promise factory in Delux

redux === boilerplate

https://github.com/Emilios1995/redux-create-module

but i think this function can completely save your life.

Its worth differentiating between two different sets of ideas going on in this thread:

  • a "full-service" redux-based framework
  • additional features in redux core

I agree with @markerikson that redux core should not become a framework in itself, and I understand why one would want to keep redux-thunk and react-redux in separate packages. However, I believe there is both room and precedent for new "helper functions" in redux core.

Redux has a fairly small API surface area: createStore, applyMiddleware, combineReducers, bindActionCreators, and compose. But most of these are convenience functions. Only createStore is strictly necessary for Redux to work. Why do any of these belong in core?

  • applyMiddleware creates a simplified interface to store enhancers. But more importantly, applyMiddleware supports the middleware _interface_ as a standard way of adding effects to redux stores. The middleware ecosystem is possible because applyMiddleware has a comparatively simple interface and allows users to run many middleware at once.

  • combineReducers provides a convenient form of reducer composition. It's far from the only way to compose reducers, but it's certainly one of the more useful patterns. But its presence in redux core means that it's not just common but _ubiquitous_. Every nontrivial Redux app that I have encountered uses combineReducers to create the root reducer. The flipside of this is that because combineReducers is the only form of reducer composition that comes with Redux, many users seem to believe its the only form of reducer composition that exists.

  • bindActionCreators reduces some of the boilerplate around dispatching actions. But perversely, bindActionCreators is also indirectly responsible for some of Redux's reputation for boilerplate. This is because the mere existence of bindActionCreators encourages the use of action creators. This is particularly visible when one compares the prevalence of action creators (which are "endorsed" by bindActionCreators) versus selectors (which are documented but have no equivalent core function). I've seen many codebases using react-redux where mapStateToProps is always "bespoke" selectors but mapDispatchToProps is always an object map of prewritten action creators.

None of these are essential to the functionality of Redux yet all of them are essential to its usability. None of these functions limit the inherent flexibility of Redux, but by reifying common patterns into callable functions they make choices less daunting. And each of these helper functions raises further questions:

  • If middleware is a simplification of store enhancers, are there other simplifications that could be widely useful?
  • What other forms of reducer composition could be implemented instead of merely documented? Reducer objects? Dependent reducers? Linear composition?
  • Would integrating reselect lead people to create and compose selectors where they hadn't before? Would this be a net positive?

Even if the answers to all of these are negative, I think we do ourselves and the community a disservice by refusing to even consider their premises.

@modernserf : First, thanks for your comments, as well as your other recent thoughts on Redux. They are all _extremely_ valuable and informative, and should probably be required reading for anyone else in this thread. So, I'll link them here:

It's very interesting that you should bring those up, because of the history involved (which I just spent a lot of time researching for my post The Tao of Redux, Part 1 - Implementation and Intent. In particular, redux-thunk was originally built right into Redux, and react-redux was only separated out later as well.

You're right that those utilities do indeed codify certain usage patterns. That said, those utilities are included because they codify the _intended_ usage patterns: composition of slice reducers, a pipeline of middleware for async behavior and other centralized behavior, and bound action creators to simplify the process of dispatching actions from components (particularly passing functions around to child components). Somewhat similarly, while Reselect isn't in the core, Dan did explicitly encourage its creation.

So yes, anyone "could" have written those, but by building them in, we've pushed people to use those specific tools in specific ways. Definitely agree on that.

Per your last three questions:

  • I _guess_ you could say that middleware are "a simplification of store enhancers", although I don't think that does them justice. Rather, it's a store enhancer for a very specialized purpose. As far as other store enhancer-related work: Dan has said that the one major part of the Redux core he would still like to modify relates to how enhancers are initialized. There's actually two "competing" PRs to change store enhancer implementation that have been sitting open for a very long time: @acdlite 's version in #1706, and @jimbolla 's version in #2214. It's also interesting to note that Jim put together a list of all known store enhancers to help analyze how the community is actually using them. I know that Jim's proposal was intended to help enhancer authors simplify commonly seen behaviors. So, that's a direction that could be explored further.
  • There's definitely plenty of other patterns for reducer composition and structure out there. I'm not sure exactly what we could do as far as "dependent" reducers, since that's much more niche use case and would seem to be pretty app-specific. On a related note, for a long time, Dan had closed any PRs that proposed adding some kind of third arg to combineReducers (and there's been a _lot_ of them), but finally allowed #1768 to go forward for a while. That PR has also stalled out.
  • The most common use case for selectors seems to be directly extracting bits of state and passing them as named props. We've also got an open PR to React-Redux for adding an object shorthand syntax for mapState, and have had numerous requests for that even though you can do the same thing with Reselect's createStructuredSelector.

As I documented in that "Tao of Redux" post, the stated goals were to keep the Redux core API as minimal as possible, and encourage an ecosystem on top of it. In addition, early in Redux's development, Andrew made a comment about the intent to "bless" certain plugins:

As gaearon said once, (I can't remember where... probably Slack) we're aiming to be like the Koa of Flux libraries. Eventually, once the community is more mature, the plan is to maintain a collection of "blessed" plugins and extensions, possibly under a reduxjs GitHub organization.

Earlier up-thread, I had proposed some kind of "easy Redux setup" abstraction lib, or something like that. Would something like that, or at least that list of "blessed" addons, be sufficient along the lines that you're thinking? (And, actually, you did too in the first comment of this thread.) If not, any additional proposals or ideas?

Eventually, once the community is more mature, the plan is to maintain a collection of "blessed" plugins and extensions, possibly under a reduxjs GitHub organization.

This is what I would do as well, though I'd probably take it a step further: I would restructure the project into a monorepo, where the current redux package becomes something like @redux/core, blessed libraries like reselect and redux-action are brought into the repo and namespace, and the redux package itself just reexports all of these packages into a single namespace.

That _sort of_ satisfies the constraint of not modifying redux core. Also, the extended version would be a strict superset of -- and therefore backwards compatible with -- the core. The newly features would be purely opt-in and have no special access to private state. As far as "bloat" goes -- each of these libraries is small to begin with, and they're written in such a way that it would be trivial to tree-shake them.

We've also got an open PR to React-Redux for adding an object shorthand syntax for mapState, and have had numerous requests for that even though you can do the same thing with Reselect's createStructuredSelector.

I think this should tell you something.

The monorepo structure I'm suggesting is basically the structure used by Lodash. Now -- Lodash is an interesting library to model after: its an enormous collection of functions, many of which are trivial to implement yourself. Nearly every function in lodash could be more flexible as a "recipe" -- groupBy is really a _pattern_ after all; who are we to say what data structures you use it on?

And every lodash function is also available in its own _a la carte_ library. There's no _functional_ benefit to import { groupBy } from "lodash" over import groupBy from "group-by". Why should I have to deal with tree-shaking when I can just install what I need?

But the experience of using Lodash is fundamentally different than using recipes or nanolibraries. You know that if you're doing something with an array and you get a feeling like you're repeating yourself, there's probably a Lodash function to do it. You don't need to look up a recipe, you don't need to install anything -- chances are you don't even need to add another import line to the file. Its removing only the tiniest amount of friction but our days are filled with moments like this and it adds up.

I hope you don't mind me adding my own 2 cents, from a different perspective.

The CQRS, Event Sourcing, and ultimately Domain Driven Design schools of thought are the greater ancestors which bore us Flux and Redux. While these influences are occasionally cited, Redux and its descendant tooling took most of their API terminology from Flux and Flux implementations which were popular at the time. This is fine, but I think we're missing some opportunity to profit from existing exposure developers may have had to those ideas.

To give two examples of the difference: In most Event Sourcing discussion I've seen, Redux Actions would be referred to as Events. Redux ActionCreators would be referred to as Commands. I think there's some added clarity and semantic that comes for free with this terminology too. Events are loggable, serializable data. Events describe what happened in the past tense. Events can be replayed. Events cannot be un-recorded. Commands have an imperative mood. Commands trigger Events.

I suspect that Event Sourcing as an architectural patterns will gain in popularity over the next decade, and I think that Redux could gain some usability, clarity and ease of use wins by aligning the API and discussion more closely with the broader community.

I think this discussion was already beaten to death: https://github.com/reactjs/redux/issues/351

Oh, I wasn't aware of that, thanks! :+1:

It looks like #351 was closed because at the time there was no action that could be taken. If we are at a juncture where we are reconsidering API decisions and the language we use, this seems like an appropriate time to resurface the idea, to me.

I can be more concrete.

Here's my idea of what an API inspired by Event Sourcing idioms might look like:

Concepts

  • Observable Event Stream
  • Event Stream has a well-typed set of supported Events
  • Commands dispatch to the Event Stream
  • One or more Clients consumes the Event Stream

API

import { createEventStream, createProjection } from 'redux';

// Initialize the event stream separately from the store.  This becomes the one
// true source of truth for your application.
const eventStream = createEventStream({
  // Commands are the only thing that we want to couple to the eventStream.  The 
  // set of events which may end up in an eventStream should be easy to predict.
  //
  // A definition like this supports static analysis inference well for 
  // consumers that can leverage it.
  increment: () => ({ type: 'INCREMENT' }),
  decrement: () => ({ type: 'DECREMENT' }),
});

// Multiple stores with disjoint or overlapping data can be used to consume the 
// same event stream.
const store = createProjection(eventStream, reducer, init);
const adminStore = createProjection(eventStream, adminReducer, init);

// We don't need a jargon term ("Middleware"), or a dedicated hook to handle 
// async anymore.  We just register more subscribers to the eventStream.
eventStream.subscribe(myCustomMiddleWare);
eventStream.subscribe(sendEventsToAnalytics);
eventStream.subscribe(logEventsForPlayback);

// Calls to commands can be wrapped with React Providers or container components 
// in the same way that Redux currently does.  They can also be called directly.
eventStream.increment();

@ajhyndman : while I do appreciate the suggestion and discussion, that bikeshed has totally been painted and the horse has fled the barn (to completely mix metaphors). Dan opted to use Flux terminology rather than CQRS/ES terms, and there are no plans to change the core APIs or the terms used for Redux concepts.

(Also, while I have zero stats to substantiate this, I would guess that at this point there are more people familiar with Redux's use of "actions" and "action creators" than with CQRS/ES terminology, at least within the JS community.)

I definitely get the impression that this conversation has been had before, and there's a strong desire not to re-open it. 😄 I won't press too much harder here. (I'd be interested in reading or continuing this conversation elsewhere, if there's a better touchpoint.)

Of course, you're also right that there is an entrenchment factor, and changing the entire API surface and terminology would be costly at this point in time.

I would still argue there's opportunity for the Event Sourcing and Redux communities to learn from each other.

  • What possible abstractions could be created that simplify the process of learning and usage, but without actually hiding Redux (and would hopefully provide a migration/learning path to "base" Redux)?

Even without borrowing the terminology or changing the API, I think there are wins we can find. For instance, I have had success introducing people to Redux by describing a store as "a state container in which currentState is a "pure function of" (it is derived from, or reduced from) an append-only list (or a stream) of Actions".

Looking further into the future, I think it's entirely possible to implement and abstract a redux-like server infrastructure (see: Apache Samza, and some of the other work from LinkedIn's engineering team). If we aspire to that, then it should also be possible to use similar abstractions for client and server state management! If we can achieve that, why not an isomorphic, persistent Redux store?

Any concepts that the JavaScript and Infrastructure communities are able to come together on, or easily map between, the more quickly I expect these opportunities to become apparent and the more expressive the abstractions.

I guess I'm just getting a little excited about some of these ideas! I hope you'll forgive me for the word dump.

Side note: It seems to be possible to create an event-stream-like wrapper API for Redux that implements the surface I suggested above!

https://github.com/ajhyndman/redux-event-stream

@ajhyndman I think the wrapper is the right way to go with the your idea there 👍

Related to the samza and linkedin engineering work you mentioned, If others have not read/watched the wonderful talk Turning the database inside-out with Apache Samza then please find an hour to do so sometime! I think I saw dan mention it in a readme or tweet at some point, This talk changed how see databases and state management forever and is a very close idealogical cousin of redux.

That video is awesome! It's also actually the second item in the list of Thanks on the redux repo's README.

Hey everyone!

Without being aware of the existence of this thread, I have developed a library that's in a way a direct answer to @markerikson's original questions and knocks off most things from @gaearon's list.

The library is called Kea:

screen shot 2017-08-06 at 13 10 03

It is an abstraction over redux, redux-saga and reselect. Except for the glue connecting them together, Kea doesn't pretend to create anything new and exposes raw redux action creators and reducers, raw redux-saga sagas and raw reselect selectors as needed. It doesn't invent new concept.

Kea supports many different modes of operation: You can use it standalone, connected to React components, inlined above React components or even dynamically connected to React components where the input to the selectors depends on the props of the React component.

I personally use it with two large applications: one marketplace for private lessons and in one huge fleet tracking software. Plus I know of people who have built their own apps with it. Because it was so useful for us, I spent considerable time cleaning it up and writing documentation.

For me this library has always greatly reduced boilerplate, while remaining true to the core of redux itself.

Anyway, enough talk, here's some code. This is an example of what I call "inline kea" - where the logic is attached to your component directly. This mode is great for when you're just getting started or as an alternative to React's setState.

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { kea } from 'kea'

@kea({
  actions: () => ({
    increment: (amount) => ({ amount }),
    decrement: (amount) => ({ amount })
  }),

  reducers: ({ actions }) => ({
    counter: [0, PropTypes.number, {
      [actions.increment]: (state, payload) => state + payload.amount,
      [actions.decrement]: (state, payload) => state - payload.amount
    }]
  }),

  selectors: ({ selectors }) => ({
    doubleCounter: [
      () => [selectors.counter],
      (counter) => counter * 2,
      PropTypes.number
    ]
  })
})
export default class Counter extends Component {
  render () {
    const { counter, doubleCounter } = this.props
    const { increment, decrement } = this.actions

    return (
      <div className='kea-counter'>
        Count: {counter}<br />
        Doublecount: {doubleCounter}<br />
        <button onClick={() => increment(1)}>Increment</button>
        <button onClick={() => decrement(1)}>Decrement</button>
      </div>
    )
  }
}

In case your app grows and more places need access to the increment and decrement actions or the counter prop, you can just split your code like so:

// counter-logic.js
import PropTypes from 'prop-types'
import { kea } from 'kea'

export default kea({
  actions: () => ({
    increment: (amount) => ({ amount }),
    decrement: (amount) => ({ amount })
  }),

  reducers: ({ actions }) => ({
    counter: [0, PropTypes.number, {
      [actions.increment]: (state, payload) => state + payload.amount,
      [actions.decrement]: (state, payload) => state - payload.amount
    }]
  }),

  selectors: ({ selectors }) => ({
    doubleCounter: [
      () => [selectors.counter],
      (counter) => counter * 2,
      PropTypes.number
    ]
  })
})
// index.js
import React, { Component } from 'react'
import { connect } from 'kea'

import counterLogic from './counter-logic'

@connect({
  actions: [
    counterLogic, [
      'increment',
      'decrement'
    ]
  ],
  props: [
    counterLogic, [
      'counter',
      'doubleCounter'
    ]
  ]
})
export default class Counter extends Component {
  render () {
    const { counter, doubleCounter } = this.props
    const { increment, decrement } = this.actions

    return (
      <div className='kea-counter'>
        Count: {counter}<br />
        Doublecount: {doubleCounter}<br />
        <button onClick={() => increment(1)}>Increment</button>
        <button onClick={() => decrement(1)}>Decrement</button>
      </div>
    )
  }
}

Please try it out and read the docs to see more about side effects through redux-saga and other features you can use.

The feedback for Kea has been overwhelmingly positive until now, quoting from an issue: "More people should use KeaJS as its making redux world a better place! 👍" :)

Thank you for your time and for reading this far! :)

Looks nice! not really liking the magic strings though 'increment'

Did this discussion move forward on another place? Of is Kea the most correct outcome which is generally accepted?

@lucfranken : no, the discussion fizzled out.

If you're looking for a higher-level abstraction over Redux, then Kea is probably a good option. If you're looking for a "Redux starter pack" that simplifies the store setup process, you may have to look around some. I know I've seen some things like that out in the wild, but we don't have any official lib like that right now.

Throwing my hat in the ring here. This is how I address this issue: https://github.com/HenrikJoreteg/redux-bundler

I personally think it's a big simplification and reduction of boilerplate.

It's how I've been building everything I build recently.

Hey, in my company we just open sourced a library which manages the network layer and removes a lot of boilerplate from Redux. It has proven successful for us, but try it out yourself: https://github.com/Brigad/redux-rest-easy/ (medium article: https://engineering.brigad.co/introducing-redux-rest-easy-6e9a91af4f59)

1_327nnvo3cuaqc-dsqpoq7w

here is my approach to reducing redux boilerplate
image

https://github.com/zhDmitry/restate

As a highly belated necroing of the thread, a while back I asked for feedback on Twitter for what "boilerplate" means to people:

https://twitter.com/acemarke/status/969040835508604929

Return of the Necro-Thread!

Per my edit of the first comment in this thread, the ideas discussed here have been turned into our Redux-Starter-Kit package. Please try it out and see how much it can help simplify your Redux usage!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

olalonde picture olalonde  ·  3Comments

vraa picture vraa  ·  3Comments

dmitry-zaets picture dmitry-zaets  ·  3Comments

ms88privat picture ms88privat  ·  3Comments

benoneal picture benoneal  ·  3Comments