Once you commit to a config based approach, you're basically closing the door to a future you can't imagine yet. I'm so tired of this short-sighted obsession with configuration some developers seem to have.
I think the author has a myopic vision for where Redux should go.
I think it's the other way around: The greatest weakness of Redux is that (unnecessarily) message passing instead of function calls are used.
Why do a `store.dispatch({type: 'INCREMENT', value: 10})` when it could be a `store.count.increment(10)`? In basically all cases every action is handled by exactly one reducer so just let the reducer be an object with methods instead of a big switch function operating on string constants.
IMHO you want the tools you use (i.e. redux) to be flexible and powerful, and write helpers, etc, to increase your ease-of-use. Otherwise, as the OP said, you're unnecessarily restricting yourself and creating a more difficult future.
The fact that Redux forces you to use events to modify the store is a useless complication which has spawned reams and reams of crazy libraries like redux-thunk, redux-saga, standard action formats, etc. The craziest thing yet is Ducks: https://medium.freecodecamp.org/scaling-your-redux-app-with-.... This is a pattern which tries as hard as possible to make using events not feel like using events, by tying each event intimately to its listener.
This library is a step in the right direction, but wrapping Redux seems unnecessary. Here's a similar approach in 100 LOC: https://github.com/didierfranc/react-stateful
Here's yet another similar approach, also in 100 LOC https://qubitproducts.github.io/tiny-atom/. Been using it for a year in a few projects.
Is there a point to spreading this piece of sage FUD?
It should not be surprising that pure functions + immutable state results in fewer errors.
The "pattern" Redux asks you to use prevents you from shooting your leg off. If you've written a sizeable application that uses global, mutable state the benefits of Redux shouldn't be surprising.
My only gripe with this eco-system is that it constantly comes up with new names and concepts for things that already exist in the FP world. I agree that the math jargon is off-putting but so is having a dozen words for the same concept. I think the latter is worse.
The library described in the article feels less like an abstraction and more like a shuffling around of syntax. Useful but misleading with a title like, Redesigning Redux.
Update: s/grip/gripe
I agree, but I think it's easy to see how it happens. It's an impediment to adoption of the project if people don't understand what you're talking about, so simplistic shorthand is used initially to get people up to speed, and then you find that your temporary names have become engraved in stone for the vast majority of your users. People being how they are, you're just as likely to get them to change editors as change terminology at that point.
Developing your own solution is one thing, and developing your own solution that's as battle tested as a popular library is another.
Baking your own solution helps you understand the core problem that a library you could have used tried to solve. But generally speaking developers that tend to do that have difficulty learning top to bottom and favor bottom up approach which leads them to developing things from scratch. That's just a personal anecdote though.
There are cases were a developer has been using a library for a while and is experienced enough to come up with better solution and cleaner APIs but I'd argue that's not the common case.
Understanding other people's code/libraries is a skill worth mastering.
https://github.com/reactjs/redux/tree/master/src
The largest and most important part is CreateStore, but at 250 lines long (mainly comments), it's shockingly simple when you take a look at it.
Damn, that was a hard pill for me to swallow just now.
https://hackernoon.com/transmission-tx-a-flux-alternative-fe...
I'm convinced it's better and simpler, but then again, I'm arguably biased.
1. The constant var `const ACTION_NAME = ...`
2. The constant's value `... = Symbol('ACTION_NAME')`
3. The "action creator" function `const doAction = arg => ...` (extra creativity to add a verb here?)
4. The action creator's return struct: `... => ({ type: ACTION_NAME, payload: arg })`
5. The reducer switch clause `case ACTION NAME:`
6. The container action to callback mapping `onClickThing: dispatch(doAction(42))`
7: The jsx/component calling the callback `onClick={onClickThing}`
compare that to The Elm Architecture, from which redux is inspired AFAIK. An action only needs to be referenced in 3 places: 1. The tag on the Msg type: `type Msg = ActionName arg | ...`
2. The `case` pattern matcher in the update function
3. The view sending said Msg/actionIt's kinda funny how redux succeeded with such heavy concepts, which is normally not the case. This is still a mistery to me.
Oh and don't get me started on HOC. Want a tooltip over your component? Sure, wrap it in tooltip HOC! Sooo much nicer than directives!
Compare this to Elm and the Elm architecture, and it seems worlds apart. Interoperability between libraries that all works in the same straightforward way, checked by the compiler.
It feels like the JS community made a lot of good decisions for the right reasons, but still ended up in a place that isn't great.
Asynchronous actions feel, to me, that they belong at the component layer where niceties such as spinners are being rendered, and then the backing store is updated with the results of the triggered action.
What drives people to put all of that into their store?
In backend, you might spin up a thread to do expensive operation like API access, and that thread has it's own database connection, to read and persist data.
With thunk, you dispatch a thunk-ed action to do expensive operation like fetching data from backend, and that action has it's own access to redux in form of `dispatch` / `getState` arguments.
In the end, my component calls `fetchData` function upon mounting. And the logic dealing with making multiple requests due to paging, or re-trying on errors then lives its own life inside a thunk action, independent from the component that spawned it.
Perhaps I'm dense, but you can serialize the current running state of a Promise in redux-thunk?
The benefit of redux-thunk (and there are obviously other alternatives that provide the same value) is that it lets you write plain functions that have nothing to do with the store, and nothing to do with components. If you put all your business logic into these plain functions, then they're really easy to test, reason about, move, refactor, etc.
> What drives people to put all of that into their store?
It's worth pointing out that redux-thunk doesn't move async code into your store. The store (i.e. reducers) is still completely synchronous.
In many React applications, components are separated into independent "controller"-layers and "view"-layers (containers and components are nomenclature I see occasionally, but the important takeaway is that both inherit from the React component class). Business logic has no place in your view components, of course, but is quite appropriate in your controller components. I don't think component implies view layer here. Even redux actions, when used, are appropriately called from the controller layer and not the view layer. Redux doesn't change anything about logical separation of concerns.
When the Flux pattern was originally introduced, it was driven home that it should not be used for everything. redux-thunk seems to encourage that you use it for everything. What do you use when global state is not necessary (or wanted)?
This is exactly how thunks work, you just fire them from your component as a Redux action. I use thunks primarily for when an action needs to get data from an API and insert it into the store (e.g. on login).
Inverting the question, why would I want my presentational components to do a bunch of extra work beyond presenting? Calling an API is not presentational.
Even Dan Abramov did it in his recent Redux presentation.
It's important that we understand why "best practices" exist but to never see them as commandments. Do what makes sense for your case, but it's your own funeral if you're deviating from best practices without having fully thought it through first.
Functional-core, imperative-shell design suggests to then use this functional core within a shell of code that handles IO, handles failure cases--effectively, anything that can fail belongs here. To me, that's the React component; I don't view React as "functional", as it's got multiple types of state even separate from Redux (and this is not a demerit, IMO). The onClick handler of a React button is no different from the command handler for a CLI application. The code in that handler makes requests of its API client (which I typically inject through context and my own HOC--like I said, React's statefulness doesn't bug me at all), it performs business logic functions on the data yielded from its API client, and it stores the result (typically with a onComplete method or the like, so my redux-connected component can dispatch an action).
I'm amenable to other ways to do that data routing (because that's really all any of this is), I just don't see why the state store is where this routing should be done and see a lot of reasons around testing and failure-rescue and general code cleanliness why I wouldn't. If you were to make HTTP requests in your SQL datastore I'd be remarkably concerned for your sanity; I don't think it makes any more sense to do so in a Redux store.
This point is not revisited. What is the performance of Rematch in comparison to Redux? I mean I guess it's more or less the same, since it's essentially Redux with less boilerplate, but it would be nice to have an explicit answer to this.
I feel like implements an action for each modification of state (or group of modifications) requires a lot of boilerplate code. In MobX, you treat your data tree as a separate entity, and simply modify it. The UI is automatically updated whenever it needs to.
Mobx isn't bad performance-wise, I think, since it only re-renders the components that are listening to the modified variables when a modification occurs.
The only advantage I see is being able to rewind the state store, which can be quite cool when debugging.
I have done some medium-sized application (only games), and I don't think I've run into any performance or readability problem with MobX. I've never done web apps with a team, though, but I don't think Redux would specially make it easier to work with other programmers.
Another big one is being able to restore the state. In Redux, it's just one giant object that can be easily serialized and use in the future. With Mobx, I'm guessing you have to write deserialize logic for your various classes.
One thing I’ve never quite understood about Redux is why we are dispatching pure data instead of a function of state. This would remove the need for reducers, since your action _is_ your reducer. Something like this...
https://gist.github.com/anonymous/a5d741c5dec81be61e0aa70820...
Would this come with any drawbacks? I could see a large application having performance issues because of a large number of function allocations, but for small applications I don’t see a downside, just a simpler API.
I have another problem with this library, which is relying on singleton pattern for communication. This way, you cannot have more than one rematch instance in one application, which can be limiting. It also makes reasoning more difficult.
[1] https://github.com/rematch/rematch/blob/master/plugins/subsc...
> In this case, subscriptions avoid the need of coupling the auth model to the profile model. Profile simply listens to an action.
I guess it’s no different to using “vanilla” redux actions but because actions contain the reducer namespace then it’s inherently coupled to another model isn’t it?
It's easier to get started, but you'll likely have really nasty problems later (testing, server side rendering, etc).
They lose me with their idea of "models" where I have a global dispatch that can be extended, like dispatch.modelName.actionName. That's just old school object-oriented thinking layered on top of redux's functional programming ideals.