7 ms·
Redesigning Redux
- danpalmer 9y agoThis looks nice, although in Redux's favour is a large ecosystem of libraries that will work with it. This highlights a problem I still have with the JS ecosystem, which is relatively tight coupling between libraries. Of course many of these libraries could be glued together manually without too much work, but with the rapidly changing ecosystem and APIs for interoperability between libraries, and the obscurity of some of the libraries (Redux being a good example), this is often just not practical. 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.
- heygrady 9y agoBabel is both the driving force of innovation and the cause of much of the fragmentation in JS. Babel allows you to use the latest language features and target the oldest environments. The trouble is that there's not a "right way" to package software to cover all of the deployment concerns. Code needs to work on Node going back several versions as well as browsers going back several versions. And for mobile/desktop websites, your code bundle size is extremely important. Rollup and Webpack 4 go a long way towards making this easier but it's nothing close to a "standard best way" to do it.
- danpalmer 9y agoThat's a good summary of the fragmentation and coupling in the build tools, but there's also the libraries themselves - React, Redux, React-Router, React-Router-Redux, etc etc.
- adamsea 9y agoI have to agree. The webpack-blocks project seemed like a step in the right direction, as it lets you 'compose' a webpack config (in theory) without side effects. I recently discovered lerna and am curious about using it to put together "safe" bundles of libraries which are commonly used together. No idea if that's actually a practical approach or not - its all in the dreaming stage.
- heygrady 9y agoRematch reminds me of Marty.js. I like some of the ideas about providing a createStore wrapper that is more zero-config and using something like redux-actions to make reducers simpler. I have been using my own wrapper around sagas to allow for dispatching effects. 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.
- mpfundstein 9y agowhats the problem of mixing the good parts of OOP with the good parts of FP?
- ballenf 9y agoThe two are just so fundamentally different that you will usually end up with a net negative taking two good things from both worlds. With the parent comment, you lose a degree of composability of the functional world while not gaining any degree of inheritability from OOP.
- davedx 9y agoI like it. Configuration should be just that. Interested to read other opinions.
- eropple 9y agoI've never really understood the point of redux-thunk. But, then, I also don't use asynchronous actions in my datastore. 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?
- Waterluvian 9y agoThe beauty of Redux and react is that you can do that and nobody's going to tell you you're bad for doing it. 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.
- eropple 9y agoI'm not saying anyone is "bad", I'm saying that I don't see why it makes sense to include the responsibility of acquiring data into the thing that stores data.
- adrice727 9y agoPart of the idea behind Redux is to keep components as pure as possible and limit side effects to one place, usually actions. You keep all async actions in components?
- eropple 9y agoI write separate business logic into which are passed their dependencies and/or the data to operate on. If you have a defined API client, you can pass that to your business logic and let it work with the API client, but to do so means that in testing you have to mock your API client. So my business logic objects tend to work on groups of domain objects (in a functional manner, usually using ImmutableJS these days). 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.
- halayli 9y ago> Consider time_saved to represent the time you may have spent developing your own solution 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.
- tomelders 9y agoAs far as libraries generally go, Redux is tiny. https://github.com/reactjs/redux/tree/master/src 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.
- hliyan 9y agoI'm afraid I'm guilty of this type of re-invention myself. Having built a very large project using Redux, I decided to develop my own alternative, not just to Redux, but to Flux itself: https://hackernoon.com/transmission-tx-a-flux-alternative-fe0630eed2a3 https://hackernoon.com/transmission-tx-a-flux-alternative-fe... I'm convinced it's better and simpler, but then again, I'm arguably biased.
- always_good 9y ago> 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. Damn, that was a hard pill for me to swallow just now.
- tomelders 9y agoThe strength of redux is that all your interactions with it are through functions. Redux today is far better than the Redux of yesteryear thanks to things like ReSelect, Thunks, Sagas, Higher Order Components etc. All these things augment Redux, and they can do so because of Redux's open embrace of the humble pure function. 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.
- badestrand 9y ago> The strength of redux is that all your interactions with it are through functions 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.
- zzzzzzzza 9y agoI missed the part where you explained why changing the reducer to an object with methods would bring any benefits, or what's bad about message passing (it seems to me off the top of my head that changing this would at the very least, break redux observable as it currently works)
- bpicolo 9y ago> to an object with methods would bring any benefits It lets static typing do a lot more work for you when using Typescript/Flow. Less to remember and less to break. Message passing is a really useful architectural choice because it lets you do powerful global operations. It's a lot crummier on the development ergonomics front.
- 9y ago
- wwqrd 9y agoSeems kind of like replacing common functional patterns with esoteric configuration objects?
- pluto9 9y agoNice. I've thought of some of these myself and implemented them as helper functions, but there are some new ideas here I hadn't thought of. Not sure I'm ready to port my project to a new library, but I may try some of these out piecemeal.
- mplewis 9y agoThis approach looks really similar to the VueX approach: synchronous reducers are mutations, and async effects are actions. Can't wait to try this out in my next React project.
- agentultra 9y ago> You don’t need to understand what a “comonad” is to use jQuery, and you shouldn’t necessarily need to comprehend functional composition to handle state management. 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
- kbenson 9y ago> 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, 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.
- Waterluvian 9y agoComing from no math background I LOVE functional programming and would love to learn what words I'm using have existing math terms.
- agentultra 9y agoA functional programming rosetta stone? Sounds challenging and interesting!
- Waterluvian 9y ago
- senoroink 9y agoThis article resonated with me fairly well. As someone who has had an extensive background in Rails and has more recently switched to 100% javascript, this kind of pattern is what the community needs. There's something to be said about getting up and running as quickly as possible. Sure you can make the argument that not everyone's application is the same, but there's a ton of similar patterns that would have been better suited to having less mental gymnastics with a simple config while still leaving the door open with FP for those that want to fine tune.
- myguysi 9y agoIsn’t one of the points of redux to decouple actions from reducers? How would you implement multiple reducers responding to a single action if your actions are tied to a reducer with a namespace?
- methyl 9y agoThis can be done using Subscriptions [1]. 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/subscriptions/README.md https://github.com/rematch/rematch/blob/master/plugins/subsc...
- dvlsg 9y agoAgreed. The singleton pattern for state is a non starter for me. Got super wary as soon as I saw `dispatch` get imported and used directly. It's easier to get started, but you'll likely have really nasty problems later (testing, server side rendering, etc).
- myguysi 9y agoAhh thanks, I was lazy and didn’t go looking for an answer. Though this line seems a bit odd.. > 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?
- deleted 9y ago[deleted]
- kenforthewin 9y agoIs Redux too complex? That's the central argument this article makes but I find it to be patently false. What drew me to redux was its simplicity: Abramov said from the beginning that it's not anything you can't code yourself, it just provides a thin framework around making changes to some base state object. Its main strength (to me) is that it forces developers to think about state changes as mutations rather than recreating a spaghetti state machine oneself.
- the_gipsy 9y agoIt's too much boilerplate. In my current project, I have to describe one action 7 times: 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/action
- pault 9y agoIt's just javascript... when I'm writing a redux-backed app I will often abstract out commonly repeated patterns in action creators and reducers. Every project is different so I just wait until things start looking predictable and refactor. Not trying to be snarky, I just see this a lot at work; people seem to be locked in to the patterns used in the redux documentation and never attempt to rewrite it as they would any other part of their app. It's quite simple (especially if you are using ES6 with its destructuring assignment) to write a function that takes an action type and a function body that eliminates most of the boilerplate.
- the_gipsy 9y ago
- chncdcksn 9y ago(To preface, I use React/Redux in all of my web applications. I think Redux is great for managing state.) 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/a5d741c5dec81be61e0aa708200b53d1 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.
- bpicolo 9y agoNow what you have is a global mutable state store instead of an "immutable" state store, as everywhere can change anything and tracking down what code does what is back to being nightmarish.
- vanderZwan 9y ago> Is Redux more performant? No. In fact, it gets slightly slower with each new action that must be handled. 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.
- castlegloom 9y agoshould have known it was a ploy for yet another library.
- woah 9y agoWhile Redux is great because it enforces a single source of truth, the whole thing with "actions" and "reducers" in Redux is a huge source of boilerplate. In almost every Redux project I have worked on, developers bring in some weird scheme to reduce the boilerplate load of having to come up with a bunch of events when all that anyone wants to do is call some functions. 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-ducks-6115955638be 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 https://github.com/didierfranc/react-stateful
- threatofrain 9y agoThe issues with Redux Sagas and etc. was unavoidable, because Redux didn't take an early stance on how to handle actions which weren't ready right away, something the article author calls differed actions. It's not because of the overabundance of boilerplate, but the LACK of guidance in the existing boilerplate.
- kn8 9y agoI share this sentiment. I always felt like Redux had the right idea (single store modified via actions), but the API was just making things unnecessarily complicated causing people to either write lots of boilerplate or come up with complicated workarounds. Here's yet another similar approach, also in 100 LOC https://qubitproducts.github.io/tiny-atom/ https://qubitproducts.github.io/tiny-atom/. Been using it for a year in a few projects.
- r00nb00m 9y agoNothing on this project solves the real problems and introduces different ones, like how to have a reducer respond to different actions. My quibble with React is that when you get into moderately complicated forms, you start sprinkling stuff all over the place to get the desired output. Take for instance the most popular form library, redux-form. It's very heavy on it's own way of doing things, and there's a lot of stuff you have to know about it to be able to read into what the hell is going on, and it's hard to keep shit in the same place. Reselect is also very heavy handed for what it does. I NEVER needed anything like reselect before redux, and it kinda went alright! In my experience, for any reasonable development with redux you have to introduce: reselect, redux-form, thunk, some sort of immutability (immutable or redux-orm), a sprinkle of compromises and plenty of boilerplate, or roll your own magic util functions (i actually rolled my own very similar stuff to rematch, for quick and dirty stuff). When you summarise all that, it's really not that simple anymore! By the time you get through all of that, you might aswell pick up angular with the added benefit of clearer best practices, nicer dependency injection (oh, but react calls it context so it's cooler!), familiarity and type safety. It'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!
- Longwelwind 9y agoCan someone explain to me why someone would choose Redux over something like MobX ? 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.
- mikewhy 9y ago> The only advantage I see is being able to rewind the state store 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.