5 ms·
What you describe in that blog post is essentially classical MVC, as far as I can see, except you don't use the term "controller" and all state changes are req
by atombender 9y ago
What you describe in that blog post is essentially classical MVC, as far as I can see, except you don't use the term "controller" and all state changes are requested via events instead of being made directly on the data model. But many MVC systems work like that -- it's not like MVC is standardized.
What this doesn't really get you compared to Redux is a consistent, serially updated, immutable state tree, and every state ("engine") becomes a somewhat ad-hoc event manager. Redux does get the concepts right, in my opinion. Your system doesn't really solve anything better, as far as I can see; it just calls actions "events" and splits the state tree into "engines".
Redux is too simplistic, though. It does nothing to solve some of the actual hard problems people face in an MVC system, such as async loading (which comes with a bunch of can of worms: Retries, error handling, idempotency, progress events, cancellation, etc.). I don't know of anything that does this well on top of Redux.
The fact that the event model and state tree are both global and shared means Redux makes it hard to encapsulate stuff, too. Encapsulation is less of any issue if there is a single instance of everything; if you have a single user controlling the page, then it makes sense that there's just one global user, and one global state for each of the user's things. But often you have multiple instances of state, and there are no tools to help you coordinate events and actions. For example, say you have a page that wants to display several charts in different boxes, and users can add new charts to the page. Every chart component "points" to which dataset it wants to render, and each dataset is a separate node in the state tree identified by ID. The actions and events then all have to refer to this dataset ID; you effectively get a little "routing layer" for actions where each action needs to look up the state by dataset ID to get the actual thing to operate on. I've not found any reusable tools to handle this.
One reason Redux doesn't scale well is just the amount of boilerplate and repetition. You can solve this with some smart meta programming, especially in TypeScript. But it's not entirely a solved problem.
- pests 9y agoFor async, do you have issues with redux-thunk? Or the generalized concept of intercepting actions with a middleware and then responding with further actions that are handled exactly as normal? For the charts example, I would just instantiate a new redux store with its own state. The reducer can be reused between chart instances. Then just dispatch other action(s) to your main redux store if you need chart state in your main store.
- lobster_johnson 9y agoI don't mean promises; thunks are fine, but don't solve the hard problems. I mean actions that start asynchronous processes such as fetching data remotely via XHR. In the end, such actions tend to be extremely similar. I tend to have groups of related actions that all such actions need. LOAD_FOO_REQUEST, LOAD_FOO_SUCCESS, LOAD_FOO_FAILURE, etc. A middleware can regex-match on the action name and count how many are in flight to render load progress. Similarly, errors can be caught that way. Idempotency is a bit harder. Often a load action has several dependencies (e.g. you want to load data based on a query and a limit) that need to be encoded in something that represents that request so that if another request is issued, it can be ignored if it's identical. This ends up being boilerplate, too. Maybe one could write a generic middleware that tracked what the in-progress request is and skipped duplicate actions. Cancelation is harder. I've never done this with Redux, as the biggest blocker is that promises can't be canceled. Last I checked, WhatWG's fetch API doesn't either. Retrying, ugh. At least this one isn't too hard to make generic, since any Redux action can, in principle, be retried. In a recent project, I rendered a notification box with a retry button if an error event embedded a retry function, which in turn just resubmitted the action. But it wasn't a very general-purpose solution.
- acemarke 9y agoLegitimate question: how would you expect Redux _or_ any other state management framework to solve those problems for you? Or, to phrase it another way: how do other frameworks or libraries actually tackle this problem already, in a way that Redux doesn't? My own apps have been fairly simple in that regard, so while I've seen a bunch of libraries to handle data fetching in various capacities, it's not an area I've had to deal with myself. So, I'm not sure what kind of "other framework handles it this way" comparisons exist.
- lobster_johnson 9y agoIt's not that other frameworks are so awesome at it, either. But other frameworks (e.g. Apple's frameworks such as Cocoa/UIKit, Core Data, etc.) typically don't provide the kind of strict state-machine purity that Redux promotes. These things are just easier to wire together imperatively, because data changes don't necessarily have to be broadcast and "reduced" the way that Redux forces you to. So some of these problems are self-inflicted.
- nemothekid 9y ago>Redux is too simplistic, though +1. Redux is a great library, but I think its a little hard to grasp at first, and it doesn't yet provide a proper framework for solving problems in a clear manner. It wasn't until a year + of using Redux and reading quite a bit code until I developed a mental framework for how to approach certain components in Redux (and whether to use Redux at all, vs. setState). Your "chart grid" example is something that I went through 3 iterations of until developing the "right" higher order component (which if you squint, could be confused for meta programming) for our use case. As a developer what you want is the right abstraction that prevents your "dataset ID" from leaking everywhere (ideally only your HoC should care about your "dataset ID", much the "redux-form" API you really don't need to interact with the "form" name once you've defined it). Once you have that, then you hopefully you have a reusable API that can scale or at least small enough to live in one developer's head without them having to wrestle with the entire application. I'm not sure how you fix it. Most of the patterns I discovered already exist under many different Medium articles.
- shados 9y agoIronically, it used to be much simpler. The community was very much around using Redux like you would event sourcing (even though thats not how it was talked about), and it was much into the FP/Elm-like patterns it provided. You could go on Discord and chat about a problem you had, and people were actively trying to solve it. It didn't take long as Redux got popular for the discussion to turn into "how to avoid boilerplate", "how to make things more SOLID" (OOP principles in Redux, lol!), and "you should use setState most of the time". That was the silent nail in the coffin. Very little progress was made in Redux patterns ever since that would not better be solved using other tools (MobX, Apollo, 16.3 context, whatever). Which is a shame: "Old school Redux" is amazing. There's a reason initially people flocked to it and said "OMG use it all the time for everything!". It's really that good. "Modern" Redux is a fragmented overcomplicated useless mess with a community that missed the point. Now get off my lawn, I guess.
- acemarke 9y agoHiya, Shados. Long time no chat :) I don't recall ever seeing comments about Redux and "SOLID". The commentary about "boilerplate" and use of `setState` has been in response to people's concerns. It's a shame that's where we've had to focus our time and attention in messaging. Can you clarify what you mean in the last paragraph? What specific concerns are you thinking of? I know you've talked in the past about the way your teams are using Redux. I'd love to see a couple posts that give some details on that. You know I'm always happy to chat about ways we can improve the docs and help the community. _Please_ ping me sometime - I'd love to hear whatever suggestions you have. You know where to find me :)
- hliyan 9y agoThanks for taking the time to go through the proposal and provide feedback. I'll try to demonstrate the idea better with a standard TodoMVC implementation. You're right: handling async events did become a headache with Redux and we were forced to wrap it with our own framework (we opted against redux-saga). You're also right about the boilerplate. With Redux, the code became a lot less confusing but a lot more tedious...
- chrisco255 9y agoHonestly I'd rather deal with boilerplate and repetition than debug nasty, non-deterministic spaghetti state.