6 ms·
In 2016, we developed several very large applications using Redux. The clarity/simplicity gains quickly evaporated as the system grew in size. The experience le
by hliyan 9y ago
In 2016, we developed several very large applications using Redux. The clarity/simplicity gains quickly evaporated as the system grew in size. The experience led me to develop an alternative [1], which I have shared here before. We used it for several production components and the productivity and testability gains were very clear.
[1] https://hackernoon.com/transmission-tx-a-flux-alternative-fe0630eed2a3 https://hackernoon.com/transmission-tx-a-flux-alternative-fe...
- atombender 9y agoWhat 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.