DEV Community

Cover image for Redux is half of a pattern (1/2)

Redux is half of a pattern (1/2)

David K. 🎹 on January 20, 2020

Redux is fantastic. Some of you might disagree, so let me tell you why. Over the last few years, Redux has popularized the idea of using message-...
Collapse
 
bradtaniguchi profile image
Brad • Edited

So just to verify my own understanding, Redux is half a pattern because it doesn't fully match up with Finite State Machines.

I think the comparison between the two is "loose" and not a strict distinction and in that sense yes Redux is half a pattern. Or more correctly it shouldn't be directly compared to FSMs, as it isn't a full representation of FSMs.

I think its possible to get Redux to be a "full pattern" that correctly replicates FSMs, but I don't think its something we would want. There's already a verboseness to Redux without specifying how side effects are handled and or how strict transitions are handled.

Also Redux maybe half a pattern, but this is half a full post 😉 (sorry couldn't help myself haha)

PS. I use NgRx, which provides effect handling and cancellations, but this isn't a Redux/NgRx feature, it an Observable/rxjs feature.

Collapse
 
icanrelate profile image
chriz • Edited

Hey Brad 👋🏻. I think what @davidkpiano is trying to convey on Redux to be a "full pattern" is instead of just thinking about the new state; it should embrace side effects as well because any non-trivial app has to deal with side effects. Right now side-effects are treated as second-class citizens and pushed to middlewares to solve the problem. Having this "disconnect" makes it a half pattern.

Collapse
 
bradtaniguchi profile image
Brad

The same could be said about how Redux doesn't handle displaying the view, or how it handles taking user actions, which are usually React's job.

Redux is a lib, not a framework in itself, it leaves the side-effects up to other libs. Its obviously a design decision that went well with React at the time. React handle the UI, Redux handles the state, async stuff handled by other libs.

Now that React has "moved on" to being able to manage state by itself via hooks, contexts, etc. Looking back it seemed like Redux wasn't "good enough", which may or may not be true, but hindsight is 2020.

The thing with comparing Redux to any other pattern is there is going to be a disconnect, as its job isn't to be a "full pattern", its job is to manage state, and it handles that well.

Thread Thread
 
luiz0x29a profile image
Real AI • Edited

But the purpose of an application shouldn't be managing state, that's a collateral.
So the fact that redux is a collateral is the failure of an ecosystem.
After all, it was made just to solve a react problem, and to solve the redux problem, you need middleware.
Perhaps there's a need for a complete solution that deal with all aspects of modelling UI.

You have your head deep down CQRS and are not seeing the forest for the trees anymore.

Thread Thread
 
bradtaniguchi profile image
Brad

Your right that managing state is collateral of the pattern React produces, where React handles the UI and now something else must handle the state.

Idk if I'd go as far to say "this is a failure of the ecosystem", when such a system is so successful at what it does. You don't need to be the perfect solution, just good enough.

Its easy to point at flaws, point at solutions to those specific flaws, and then state the original system is a failure. As no system is perfect, but the real question is what kind of benefit do you get out of the solution, and is it worth it to move? Are the "failures" that bad?

I'm not sure what a "complete solution" looks likes, idk if anyone does. I'd even say such might just be the "perfections fallacy framework".

Thread Thread
 
luiz0x29a profile image
Real AI

"when such a system is so successful at what it does"
I wouldn't call such a system so successful, it does solve a specific problem, albeit also creating more problems. But that could be said about anything.
That's why its a failure of the ecosystem, each system in itself is good and fine, but the sum of all them is less than great.

" Its easy to point at flaws, point at solutions to those specific flaws, and then state the original system is a failure. "
Pointing failures is a bold action when everyone is blinded by hype.

"As no system is perfect, but the real question is what kind of benefit do you get out of the solution, and is it worth it to move? Are the "failures" that bad? "
That's the exact question I started asking, is it worth using React, yes, the failures are bad, in as much as I'm seriously considering parting ways with using react to build user interfaces.
Indeed, I'm already did that, I only use Inferno now, because I migrated to another entire ecosystem, I'm either using ReasonML or Fable with Elm approach for constructing user interfaces and managing messaging and state.
React was only doing the rendering, not even components and JSX I was using, only functions and render.

"perfections fallacy framework"
The entire idea of HTML+JS+CSS is a perfection framework, perhaps I just wanted a canvas and webassembly to make applications, because HTML was made for documents.

Ecosystem failure, yes it is.
Pointing errors is easy, pointing the solution is actual hard.

This industry is built on this idea of disrupting but it won't release the bone, there's no meat anymore in HTML...

(time to bring back VRML, just kidding, but seriously considering)

Thread Thread
 
bradtaniguchi profile image
Brad

"Pointing failures is a bold action when everyone is blinded by hype."
I'm not sure what being "bold" with claims has to do with anything beyond ego. Nor am I sure if its reasonable to say React is "blinding by hype" at this point. At a bare minimum, hype pays the bills since React is still popular and being asked for in the job market. At a worse case its a straw man argument ("I'm right because everyone else is blind!")

"The entire idea of HTML+JS+CSS is a perfection framework, perhaps I just wanted a canvas and webassembly to make applications, because HTML was made for documents."
I don't think you understood what I meant by "perfectionist fallacy framework". The perfectionist fallacy refers to the idea "something perfect exists". I never claimed HTML+JS+CSS is a "perfect framework", nor did I claim it itself is a framework.

Furthermore, you claim "the failures of React are bad", then provide 1 example, where "React was only doing the rendering...". Which by itself sounds exactly like what React should be doing. I'm not sure if that is suppose to provide support to the claim "React is a failure", or just provide support for the alternatives you directly mention your moving toward. Unless of course your trying to compare apples to oranges, where you compare React by itself to a more "full" solution.

"This industry is built on this idea of disrupting but it won't release the bone, there's no meat anymore in HTML..."
I don't see how HTML has anything to do with disruptive tech. Or more relevantly, what frameworks do to disrupt really anything other developer viewpoints and egos.

"That's why its a failure of the ecosystem, each system in itself is good and fine, but the sum of all them is less than great."
I can see the ecosystem being multiple parts being itself an issue, but I can also see it as an advantage for those with the manpower and capabilities to leverage each part correctly together, and interchange each part. To label the entire ecosystem a failure, and together "less than great" only works if you give solid examples for all cases. At a bare minimum, it should be agreeable the React ecosystem is successful when used correctly, and its possible you can screw it up with the freedom it gives.

Thread Thread
 
luiz0x29a profile image
Real AI • Edited

"I'm not sure what being "bold" with claims has to do with anything beyond ego."
It has to do with going against the crowd, that has nothing to do with being motivated by ego.
Its just a strategy, following the safe path versus going slicing the vegetation into the unknown, which has risks, but also benefits.

"Nor am I sure if its reasonable to say React is "blinding by hype" at this point"
Are we past this idea of using React everywhere, so we can start considering where its applicable ? I think not yet.
See all this discussion I caused only because I considered possibly it is not a good answer, and even considered the entire web as not the answer for the problem.

"hype pays the bills"
great argument, paying the bills, couldn't care less about anything technical do we ? better stick with ego motivation, at least it makes sense, anything could pay the bills, even flipping burgers, are we discussing merits of technology choice or what?

"React is still popular and being asked for in the job market"
Oh, the appeal for popularity, that's also a fallacy, its not an argument.

"I'm right because everyone else is blind"
Its not about being right, the moment I start choosing the technology I use based on the problem I need to solve, when others choose based on hype/popularity/job_market, I'm a bit more right, at least in my specific context/use.
I could be wrong or not, but its a bigger mistake to delegate important choices to the crowd.
Its the same argument as the 'ol you can't go wrong by choosing IBM/Microsoft/Oracle or whatever, yeah, technically no, but realistically you are also not right in your choice, popularity is the worst argument for choosing anything.

"I don't think you understood what I meant by "perfectionist fallacy framework". The perfectionist fallacy refers to the idea "something perfect exists". I never claimed HTML+JS+CSS is a "perfect framework", nor did I claim it itself is a framework. "
No, I understood it, I am seriously claiming that HTML/JS/CSS is not perfect, which should've been obvious, people just assume that without even realized, good think you didn't, because that's not what people usually do, it was just a counter-point, perhaps badly executed.
And HTML/JS/CSS are a framework, by definition, they are outside your code, and which totally dictate how your code is organized, they are not libraries, the "web" is a framework, a very bad one for making applications, but a good one for documents, which it was intended to.
And its not about being perfect, the problem with the web tech is that it is the lowest common demonominator, we don't want perfection, that doesn't mean we should accept the lowest possible solution either.
Something better must be possible, I'm betting my business on it.
Some people seem to be too invested to even consider the possibility of something better than the web...
That's their problem, not mine.

" "React was only doing the rendering...". Which by itself sounds exactly like what React should be doing. "
If React only did that... It also has hooks and try to manage state, one of the reasons I ditched it, I only need rendering, not even VDom, what if this idea of having the VDom is a wrong one?
Then that could be considered as a failure, my failure in using React to being with. To be fair, that's not React's failure, but kind of is the ecosystem failure if you go with the crowds (and they are part of an ecosystem) and just use what others are using because of popularity, that was my mistake, it won't happen again.

That wasn't supposed to be an example of were React fails. that's what I mean by ecosystem. React on its own is not bad, but it does create more problems to be solved, that's accidental complexity.
The web is rotten with accidental complexity.
My lens of failure is very simple, is a tool fit for that purpose, React is fit for its purpose, although with too much overhead.
What I claim failure is the composition of the entire ecosystem of modern web-applications, because its clearly not made for that purpose.
Of course React doesn't have anything to do with that, its just that I even consider if the "purpose" of what React achieves was even necessary. That's the question, and there's where's the biggest failure, we are using one thing made for one purpose to another purpose and claiming it is good, when there are clearly examples (like the post) which shows the problems.

When you decision, which should've been no brainer get this reaction...
I've just decided to go against the idea of needing React, not because it is a failure, but because this entire approach for solving this problem is a failure.


"I can see the ecosystem being multiple parts being itself an issue, but I can also see it as an advantage for those with the manpower and capabilities to leverage each part correctly together, "

You have to think hollistically for that, the problem is not the system being multiple parts, I can't context the idea of specialization.
The problem is that the system was made for other purpose and we got stuck in a local maxima.
That's why I said it is not great.

"To label the entire ecosystem a failure"
But it is a failure of being fit for purpose, because we didn't knew the purpose in 2004 when this insanity of using the Web document model to create applications, it all started with AJAX, and even earlier, with DHTML.

No, I don't need to give solid examples for all cases, I could do it, but then this will turn into a master's dissertation, and I have better things to do.

"At a bare minimum, it should be agreeable the React ecosystem is successful when used correctly"
I will agree with that, the system can be used succesful, albeit you have to either ignore the problems it creates, or live with them. I decided I had enough.

"freedom it gives"
There's more freedom in not being restricted by it actually, that's why I went back to "VannilaJS".

Collapse
 
rodocite profile image
Rodolfo Yabut • Edited

For such a long analysis, the article completely misses the point about Redux being an event store that implicitly leverages several good patterns that are seen in highly-available distributed systems.

  • it forces you to separate your write model (dispatch) from your read model (props). this is known as CQRS.

  • the reducers are actually referred to as projections in event sourcing. reducers/projections are just aggregate data accumulated from events and are considered part of the read model. the strategy of doing the reduction on each event allows you to present aggregated data in "realtime". the alternative would be to iterate through large sets per read or write. it's a per-event, "realtime" .reduce()

  • Redux + Thunks was terrible. But Redux + Sagas more closely represent event sourcing as you see it in distributed systems. with events thought of as streams (or aggregates in the Domain-driven Design world). Sagas are actually referred to in the event sourcing world as either process handlers or sagas and are responsible for complex stream handling.

  • event stores and blockchains use roughly the same structure conceptually: Append only File

except with blockchains, each new insertion is hashed with the previous hash in order to keep the log cryptographically verifiable and immutable-- and is needed for Byzantine Fault Tolerance (so each node can be verified very quickly as a good actor or not).

smart contracts -- the reader contracts have similar logic to reducers. write contracts all look like event dispatch.

consensus algorithms do exist outside of blockchain tech. and with data structures that are considered high availability (they cluster and replicate), you will see that they are all AoF or KV stores or a combination of both.

  • most databases under the hood actually have a very similar event sourcing structure referred to as WAL -- write-ahead logging.

  • the typical event payload is the same in all these systems {eventType, metadata}

Just take a look at Redux w/ Sagas, Greg Young's Event Store, any blockchain, and even Kafka. They are all fundamentally similar. And since they all source from a log file, they can all replay state.

Also, Redux is an opt-in and can be as specific as you want based on how you tag your events. Your entire app doesn't need to respond if you slice your state right. And the structure itself has proven to be efficient. Event sourcing systems can handle millions of concurrent streams. Sure, people might argue there are optimizations in infrastructure that are NOT in Redux, but the structure itself is pretty ideal for streams if you write good handlers and partition your aggregates in a sensible way. The people who wrote Redux Saga totally see it.

I'm actually surprised that Abramov doesn't know the source inspiration for the thing that made him famous in the community.

Collapse
 
davidkpiano profile image
David K. 🎹

There's two parts to this article, Rodolfo. Be patient.

Collapse
 
rodocite profile image
Comment marked as low quality/non-constructive by the community. View Code of Conduct
Rodolfo Yabut

Excuse me? I'm sure you think I'm rude, but this article is unnecessarily long and didn't touch on much other than how confused people are (which surprisingly include people on the React team). My 1 comment has far more insight into the implementation than your entire article. Why don't you just take the criticism? It's obvious you're not crystal clear on this either and you went down a rabbit hole and will probably end up using a lot of the insight I just gave you (and I'm sure what other commenters have given you).

In fact the only reason I signed up was to comment at how bad the article is because I felt that it was prolific enough that it does people who want insight a disservice. Will you really make an awesome part 2 that isn't an inane, self-serving, ambiguous CS analysis of a practical pattern that will help clarify it for people and give them insight on when and how to use it? I really hope so because you have a great opportunity here to teach.

Hint: Immutability and finite state machines will probably confuse people more. Save that for part 3 or a deep dive on structures. Event sourcing is a pretty misunderstood pattern and sub patterns have emerged.

Thread Thread
 
davidkpiano profile image
David K. 🎹

Redux is definitely not used for event sourcing, nor is event sourcing applicable for most of the applications Redux is used for.

Thanks for your comments, though!

Thread Thread
 
rodocite profile image
Rodolfo Yabut • Edited

You don't understand the pattern. Don't mislead an entire group of people.

You are storing events. There is a literal "store" prop passed into Provider. Therefore it is a "source" of events and not just ephemeral event-driven programming like you see in video games.

It is event sourcing.

Thread Thread
 
davidkpiano profile image
David K. 🎹

You just proved to me that you don't understand event sourcing.

You don't store state with ES (except for snapshots etc). You store events. That is impractical for most apps.

Thread Thread
 
rodocite profile image
Rodolfo Yabut

I think you're just Googling event sourcing and trying to disprove me. Because you can most definitely store "state" with the pattern and it is done quite often. They're called projections. State storage is your read model, actually. For people who just need to read from streams, they don't need projections in-line with the event store. They do aggregations in another service. We just see the entire flow in Redux.

I mean you're kind of proving to me you haven't thought of this pattern much and you're about to try and teach people.

Thread Thread
 
davidkpiano profile image
David K. 🎹

Go write a post then.

Thread Thread
 
rodocite profile image
Rodolfo Yabut • Edited

I am working on it.

But I'm not prolific and it is something I know that gets even the event sourcing community heated. Only reason why I chimed in is because you are prolific and I think you have a chance at giving a ton of people an aha moment that isn't in the wrong direction.

If you want to research for your follow up blog post, check out how similar these are to Redux and Redux sagas:

In particular, the event handlers:
github.com/commanded/commanded

Projections:
eventstore.org/docs/projections/in...

Discussion on sagas and handlers:
stackoverflow.com/questions/342846...

Can't deny how close the patterns match up. I mean the EventStore Projection API is basically what we know as a reducer. Event Store was made back in 2012.

But I will say that in infrastructure, people misunderstand and do all sorts of weird event sourcing patterns. If you had a good evented model in your infrastructure that represented state pretty well, complex stream transforms in Redux (and Redux itself) starts to disappear because the work has been done for the UI.

The only reason why we needed Redux in the first place was because we needed to model some type of complex state representation that the backend didn't give us. If the state is computed in the infrastructure and the UI as a service just receives it, where is the role in Redux other than acting as a global store or a pubsub? And even then, with GraphQL and Apollo + cache, Redux's complexity and importance starts to disappear.

Thread Thread
 
boubiyeah profile image
Alex Galays • Edited

Time to eat some much needed humble pie Rodolfo; you want to push your point of view so hard...
Redux is absolutely not event sourcing (I used both extensively)
The only thing in common is that there are "events", something extremely common and found in pretty much all codebases (and the dreaded DOM)

No frontend keep all the events from last week around for the logged user, it's slow enough as it is. If you don't keep all the events, then it's not sourcing, just event driven (like almost all UI paradigms from the last decades)
Trying to find similarities between very different architectures might help you learn new things, but there's not point pushing this tool down people's throat.

Thread Thread
 
rodocite profile image
Rodolfo Yabut • Edited

You wanna mansplain me more? Maybe you can explain how React works, too. Insult me because you think I'm using analogies as learning tools (although that is absolutely completely valid)?

Actually, I think you should eat some humble pie. I am bringing up a legitimate comparison and I have used both extensively as well.

Your argument doesn't make sense and is just a nitpick. You in fact DO keep all the events in context of the lifetime of your frontend. If you do not choose to persist them after the browser closes or refreshes, that's your choice. But the implementation itself matches event sourcing. You are using an event store, running projections off of them, and using those projections in your read model. You can even replay your events to regenerate your state. If it looks like a duck, talks like a duck?

Also, there are event streams that terminate in event sourcing, you know. You could think of a browser session as a unique event stream with a prefix "ui-1", "ui-2", etc.

But again, the main point is: in context of the UI's session lifetime, the events are not ephemeral and you are indeed keeping all the events.

As for speed, the implementation of Redux might be slow, but updating state per event is one of the reasons why people use event sourcing in infrastructure-- to do relatively complex and flexible compute "realtime" without having to iterate through large sets.

The "slowness" is due to the growing event store which is a problem in ALL event sourcing implementations. In infrastructure, you typically cold storage "really old events".

If you don't have the flexibility to see the valid comparison, I doubt you've used both as much as you say or you haven't really thought about it. The similarities go FAR beyond analogies and learning helpers.

In fact, if you took the event storage itself and persisted it, you now have a way to recover complete session state through replay. Though UI state isn't so complex or critical that you can't just persist the state itself versus doing a replay from the events. It would be useful if you wanted to track clickstream and state transitions beyond 1 session. Which some people might want to do.

As I said in a previous post, other than purely UI state, we needed Redux to model complex domain state that the infrastructure should have already given us. If the we already had that state, we wouldn't need to mirror what many scaled infrastructures use for our frontend. The reason you remove Redux is you already HAVE the state you need. But I think the pattern itself is a pretty important entry drug (although contentious because people do it in so many different ways) concept that could help launch people who know React and have only worked in React to understand how distributed systems manage state and data.

React is already very good training in SOLID. And as a result, a lot of the component patterns you see fundamentally match up closely with supervision tree models in distributed systems. Now THIS next statement is me using an analogy, but if you diagram a supervision tree and don't label it and just describe it in terms of supervision tree definitions, then tell people to label it in terms of React Components, they will surprisingly know how to.

Thread Thread
 
joeyorlando profile image
Joey Orlando

@Rodolfo you’re my hero 🤙🏼

Thread Thread
 
luiz0x29a profile image
Real AI

" If the state is computed in the infrastructure and the UI as a service just receives it, where is the role in Redux other than acting as a global store or a pubsub? And even then, with GraphQL and Apollo + cache, Redux's complexity and importance starts to disappear. "

Well, you are not wrong about this.
That has nothing to do with the fact of things being event store or not, its just that you don't need to store collateral state if you model the data for the consumer perspective, instead of the producer, this should be obvious.

When all you have is an "EventStore", everything looks like events.

The only problem with Eventstore and CQRS is that FSM are actually the generalization, not the other way around.

With the David actually got right, and you didn't, Rodolfo.

Collapse
 
blocka profile image
Avi Block

Saying redux is like event sourcing, is like saying redux is like a state machine. It's definitely possible to model redux like a state machine, but there's no inherent constraint in redux that promotes this.

The only constraint redux has is that reducers are pure...there's no constraint on actions. Actions can be modeled as commands or events.

Then again, if they're events, where are the commands? And if they're commands, where are the events?

Also where is CQRS? Aggregates?

"Event-driven" does not mean "event sourcing".

Collapse
 
luiz0x29a profile image
Real AI • Edited

After studying more about virtual finite state machines I would say that these patterns that are "good"(citation needed) for distributed systems are just specific instances of something more generic.
I mean, they do work, but they are not as general as they could be, they are specific cases of a more general pattern.
I don't think the author missed "point about Redux being an event store", that point is actually irrelevant for understanding FSMs as they are the more general case, which was the entire point of the article, and I don't think the poster is wrong.

This is what's causing the clash, you are doing something like bottom-up, when the reasoning was top-down.

Collapse
 
oliverradini profile image
OliverRadini

Clearly a very well thought out post, with some interesting points.

I'm struggling to agree with the main theme here, though; I can't understand why the fact that Redux doesn't explicitly follow the state machine pattern is a problem. Redux is a library for state management, right? Some types of state might be best represented as state machines, but is this the only way to represent state? Probably not. And I'm guessing that state machines aren't always the best way to manage state, either.

State machines are one type of abstraction which one might apply over state, and whatever-pattern-redux-should-be-labelled-with is another. Pick your poison. Yes, you can introduce bugs into an application with Redux, some of which might be prevented with a state machine pattern, but I imagine the reverse is true, also.

What I personally like most about Redux is it forces state into a subset of the language which is easy to manage. Pure functions and values are simple, and easy to reason about.

The main problems I see with Redux are:

  1. Trying to do too much with middleware
  2. Only using Redux because you wanted to map props in deeply nested components

Regarding 1., I don't see why Redux should need to contain any async logic at all. Put it elsewhere. Regarding 2., there are better ways to do this and it's not really the point of using Redux at all.

Collapse
 
davidkpiano profile image
David K. 🎹

I don't see why Redux should need to contain any async logic at all. Put it elsewhere.

This is the main problem I'm trying to highlight. Fragmented logic because of an incomplete pattern.

Collapse
 
oliverradini profile image
OliverRadini

Sure - but if Redux took a more holistic approach, and went so far as to implement state machines, then it'd be a different library. There are options out there which do give you state machines in javascript, for instance XState - which you develop (as an aside, I think it'd be better if you state quite explicitly that you're the developer of that library, for the sake of full disclosure).

In the example you give of Reddit's loading state, we might equally view whether or not there were no results as an item of derived state. It can be derived from two items of required state; the set of results, and the loading state of the application. Something like:

const hasNoResults = ({ results, loading }) => !loading
  && results.length === 0;

Not saying this is a better approach, just a different one, and a different solution to the same problem. Different perspectives will yield different results and it's hard to give very clear guidance as to which is better in all situations, and I think that calling Redux half a pattern is a little misleading. It's half of the pattern you decided to compare it against, or, it isn't a pattern which could be called state machines.

Thread Thread
 
davidkpiano profile image
David K. 🎹

as an aside, I think it'd be better if you state quite explicitly that you're the developer of that library, for the sake of full disclosure

I didn't want to do this because I just wanted to talk about Redux and state machines, instead of comparing/promoting my library. I didn't think that would be fair.

Not saying this is a better approach, just a different one, and a different solution to the same problem.

Of course! You can definitely write your logic like this, without explicit state machines, and cover all the edge cases, and have it work just as well as an explicit state machine approach. It just becomes much harder to "reason about" and understand how the app flows from a higher level. But if you don't care about that (and you just want to ship stuff the way you know best), then that's fine.

It's half of the pattern you decided to compare it against, or, it isn't a pattern which could be called state machines.

No, it's half of a pattern because it provides lightly opinionated "scaffolding" (atomic global state, reducers are pure functions with no constraints except for purity, side-effects via middleware somehow) but the other half of the pattern is what the user needs to implement themselves. They need to "fill in the blanks" and that leads to them constantly half-inventing state machines, whether they know it or not.

Like I said, the comparison against state machines isn't an arbitrary choice. Our UIs are state machines.

Thread Thread
 
oliverradini profile image
OliverRadini

I do still think it's relevant to mention that you've developed that library. Not to say that it makes you biased; it makes you informed on state machines and the strengths/weaknesses of that pattern.

Anything that's half a pattern could be considered a whole of some other pattern. The boundaries between patterns are completely arbitrary and we can draw the lines where we like.

I wholly disagree that using derived state is less reasonable than state machines. The two aren't mutually exclusive in any case, but both patterns have their supporters and theory, both patterns are likely equally able to be reasoned about. Indeed both are likely to be little more than constraints which can be applied to state management, and it's quite possible that both make an application's state a more reasonable proposition.

Thread Thread
 
davidkpiano profile image
David K. 🎹

I wholly disagree that using derived state is less reasonable than state machines.

But it is! The proof is that derived state can determine "what state are we in", and finite state machines can also determine "now that we're in this state, what can happen next?" That part is valuable information, and very hard to determine without some sort of abstract model.

Thread Thread
 
oliverradini profile image
OliverRadini

Indeed you're correct; though to reiterate what I said in the last reply, both are restrictions we can apply onto state which are likely to make it more reasonable. I don't think they're anything like mutually exclusive.

I had meant for derived state to be little more of an example of a different pattern which you could measure Redux and/or state machines against. My point was intended to be that these types of comparisons depend on the reference points of those comparisons; Redux is 100% of one pattern and 50% of another, and 0-99% of yet more patterns.

Collapse
 
jontymc profile image
Jonathan Curtis

Interesting post and made me clarify my thoughts about Redux. I would argue that actually Redux is trying to be more than it should and this is a big source of confusion to people trying to learn it.

For example, whether you use a state machine or not should have nothing to do with the state itself. Why do you need a reducer? Just have your own state machine logic and update the state using events from the state machine.

Redux should just really be about updating state with events, reducers muddy the waters. I think this muddiness comes from its origins of use with react and specifically the idea of uni-directional data flow. Practically, state management should be agnostic of uni-directional data flow, but this is entrenched in Redux design.

Middleware and async actions also lead people down the wrong path. Mostly this is just code you should have in plain code you write. Eg, loading data should be outside of state management. Load data, update state. Don't complicated with middleware.

I prefer libraries that do one thing well. A state management system should just be about events, state and subscribing to changes. Redux has confusing concepts like reducers, async actions and middleware, not to mention actions are events. It would be much better split into two parts - state management and state machine, although arguable whether you need a library for the second part.

Collapse
 
jontymc profile image
Jonathan Curtis • Edited

I would also argue that the loading state of an api request does not belong in the global state. This is local to a single component, putting local un-shared state in the global state makes things more complicated.

Collapse
 
davidkpiano profile image
David K. 🎹

Completely agree. I'll cover this more in the second article. Nothing belongs to global state because nothing truly is global state.

Thread Thread
 
boubiyeah profile image
Alex Galays

That's my main gripe with redux.

I think no more than 20-30% of state should be global: logged user and its organization, resource cache and that's pretty much it.
It's insane to have to go through all the selectors for some fairly local piece of state changed.

Thread Thread
 
luiz0x29a profile image
Real AI

Partial update of state in immutable pure functional applications is pretty much open research.
We actually need machinery to deal with it, you can't just update the global state every time.
You just happen to now have the same problem again, instead of React and DOM, you reintroduced it with your entire application global state in place of the DOM, and now need some form of React to deal with it.

Perhaps the entire perspective was wrong.
Virtual Finite State machines is what I'm looking into, they contain the mutable state, the are pure in the way that state+event change state, and they allow for side effects, because without side-effects:

"Haskell is a completely useless language because in the end a program with no effects, there's no point in running it, is a useless program.
And you know, you have this black box, and you press go, and it gets hot, but there's no output.
So why did you run the program" - Simon Peyton Jones

youtube.com/watch?v=iSmkqocn0oQ

 
davidkpiano profile image
David K. 🎹

The low-level API is fine... that it enforces a single atomic global store is where unnecessary complexity grows.

More on that in part 2!

Collapse
 
xstos profile image
Chris C • Edited

I inherited a react/redux app here at work, and I found the organizational structure really hard to follow due to how everything was coupled. It had code smells all over it. The ideals were good, but the implementation left a lot to be desired.

Code is for people to read, so if patterns are confusing I disregard them and ignore "the experts". Amusingly the best and most understandable software pattern i've happened on in my career has been a mix of all these patterns.

C# has part of the answer with extension methods. You define data inside classes, and then create transformers (extension methods) between types (basically your entire codebase is a giant LINQ library). If the data being operated on is immutable, and you undo all the mistakes the OO architects created by getting rid of nulls, using discriminating unions, not using exceptions (or wrapping them at the lowest possible levels) and much more (fsharpforfunandprofit.com/fppatterns/) you end up with really modular code.

For the UI you use a hydrid model, where you make your datastructures implicitly serializable (as lispy as possible) with react-ish UI generator functions to render atoms of UI (again you end up with a LINQ library of functions to build UI very similar to react but without needing the jsx syntax). If you memoize the important bits, then your UI doesnt get recreated over and over. I then use a global event pipe for all the events. It all works great.

The only problem is, I've met precisely zero people that program this way, by stealing a bunch of patterns and mashing them together. FP abstractions sometimes get so into the weeds, that it's hard to read, so you have to strike a balance between all these aspects, by continuous refactoring and no upfront design. This is obviously really hard with more than one developer as the bad habits everybody has breaks all this stuff in a myriad of ways.

Rich Hickey has a talk where he states "simple is hard". That's what I do. I make everything so stupidly simple, it's modular as hell. Then I go to work, and get inundated with OO spaghetti code, and it saps the fun right outta life...

Collapse
 
oliverradini profile image
OliverRadini

Do you think that Redux falls into the bracket of complex and easy, rather than difficult and simple? (to use the categories defined in Rich Hickey's talk)

For me, it falls very much within the bucket 'simple and difficult'. It has a single representation of state, which is a value. State can only be updated by reducers, which are pure functions. Reducers can only be triggered by actions, which are just data.

I'm struggling to see what part of that is complex, and I've spent a lot of time trying to think about whether or not Redux is simple or complex. How it gets integrated with React components might be a little more complicated on the surface, but again this is basically always done using higher order functions, which are an abstraction of sorts, but relatively straight forward.

Please do correct me where I'm wrong because I'm not trying to just pick holes in what you say here, I agree with the rest of your comment entirely, I'm just trying to objectively question where Redux falls along the simple-complex spectrum.

I found that Redux became a lot more appealing to me personally once I sat down and properly read the paper 'Out of the Tar Pit'. I like the idea of silo-ing state and restricting its manipulation to a subset of a language.

Collapse
 
xstos profile image
Chris C • Edited

Hmm good question. I think the conceptual model is definitely simple, but as you say, since it's only a piece of the pattern, many devs opt for obtuse boilerplate (ignoring DRY), rather than buckling down to build higher level abstractions to achieve KISS and make code easy to reason about and navigate/digest. They are afraid to write utility code, or code generators (because it's perceived as hard) to essentially be as lazy as possible and write the least code. So they are lazy and wasteful instead and accrue tech debt and increase illegibility. Most devs don't write their code for humans to read with the simplest conceptual model. I love plain functions. Simple data, single argument, no side effects, simple data out. Most code should literally look like an excel spreadsheet. Classes are table rows, lists of classes are tables, formulas are static mapping methods to new tables. Unidirectional and simple.

My gut blames encapsulation as well. If one can't easily open an engine, and mod it, then we get these weird unreadable codebases like Dan Abramov was referring to. Everybody is solving a small slice of the problem and a lot of the problems internal to the project are hidden and not exposed to the community. I noticed this when I re-implemented entire utility libraries that I later noticed Microsoft had in their own reference source (often with the same signatures), but had not shared. So we get all these npm snapshots of pieces of the problem and people repeating the same solutions all over the place and they're hard to hook together and model cohesively. A big pet peeve of mine is that all code should be public and modifiable from anywhere. Swim not dive.

I suspect encapsulation doesn't scale well, because when one is 200 levels up from a problem (the npm packages folder depth is insane), perspective is lost. My gut says that if there was an R&D group that built an app from the ground up modeling everything like a physicist would, be it temporal logic (reactive extensions), asynchrony (promises), UI component models like react, the mapping of state to UI with redux, the low level rendering of graphics, etc. All those things would need to be in the same codebase to evolve together (as a modelling exercise). The fact that our dev environments don't come with hygienic macros or simple code generators built in (controversial I know), means we can't create DSLs to solve all our issues with minimal bloat when the libraries don't quite capture our requirements.

Unfortunately the difficult nature of code sharing and collaboration fragments this and separates concerns that perhaps shouldn't be separated. Not sure much of this diatribe helps anything though D:

PS: I am not a DRY extremist, I lazily model problems by how many times I see code repeating. Ad-hoc, on the fly with little forethought. Only when my bad code spidey sense tingles, do i step back and think about things. The repetition hints at missing conceptual models and informs what abstractions I create. This is really hard to do collaboratively however, as it requires training people to use a ruthless refactoring algorithm while they code. I still haven't had any luck teaching people how to do this at scale. I've never really taught people anything en-masse, so i'm sitting here on my high horse when perhaps I should be trying to convert a few people at work and see what comes of it. :)

Collapse
 
luiz0x29a profile image
Real AI

Redux is simple like assembly, which is not good.

Collapse
 
luiz0x29a profile image
Real AI • Edited

Actually, patterns are an anti-pattern that demonstrate clear as the day that the language is failing at modelling what should be modelling clearly and in a syntactical and understood way.

As someone who studies programming language development, every time I see pattern, I see a broken programming language or library.

Now knowing that, you are never going to be seeing patterns the same way for your entire life.

We shouldn't need to have patterns, we should be able to express an idea directly.
English doesn't have patterns, does it ? it has syntax, and semantics, but no pattern (well, it has some, but mostly they are legacy, we just ignore them anyway)

Collapse
 
luiz0x29a profile image
Real AI • Edited

" I've met precisely zero people that program this way. "
Meet me, you have 1 person now.

Come over to F#+Fable, we have more persons, and we are trying to do a thing here.

Collapse
 
beezlebuddy profile image
Ryan

Thanks for this, David. I know these has been some shade cast in some of the comments (yOu ArE mIsSiNg ThE pOiNt), but I love this thinking outside the box and saying "redux can be this", and I love using finite state machines in other code basis, so there's a good chance I am going to march to the beat you are banging out.

I'm very much looking forward to your next post.

One question, though: I've always daydreamed about working in Orlando. Do you work remotely from there or do you "go to work" in an office there? I didn't think there were many dev jobs down there.

Collapse
 
helloitsjoe profile image
Joe Boyle

Great post! I went from "why is he calling actions events" to "why weren't actions called events in the first place??" I love the idea of using a status enum instead of a combination of loading, error, data, etc.

One thing... Unless I misunderstand what you're saying about canceling an async thunk request, you can just ignore the success action if the fetch has been canceled:

  switch (action.type) {
    case "FETCHING":
      return { ...state, status: "fetching" };
    case "FETCH_SUCCESS": {
      if (state.status !== "fetching") {
        return state;
      }
      return { ...state, status: "resolved", users: action.payload };
    }
    case "CANCEL":
      return { status: "idle", users: [] };
    default:
      return state;
  }

Here's a working codesandbox: codesandbox.io/s/redux-thunk-cance...

Looking forward to part 2!

Collapse
 
letanthanhtai profile image
Le Tan Thanh Tai • Edited

Very well written. I’ve just come up the very same idea that redux middleware as state transition functions and shared it to my colleagues then I came to this articles.

Keep up your good works.

P/S: On the other hand, I also think that a middleware is really a controller which is missed in MVC model. A very limited controller which is responsible for UI triggered changes and API triggered changes

 
davidkpiano profile image
David K. 🎹

Okay, please keep using Redux however you'd like, thank you for your comments

Thread Thread
 
bradtaniguchi profile image
Brad

I do find it fascinating how there are very drastic responses to this post so far I must say. Some people agree that Redux is inherently "wrong", and other argue its inherently "right".

I'm like many, waiting for part two :D

Thread Thread
 
blocka profile image
Avi Block

The main advantage is to have 1 SINGLE "ROOT" of THRUTH!

Not sure about you, but in the types of apps that I write, I can not see any advantages of having a "single source of truth". In general, the complexity is always either at the page level, not the app level (the app is essentially just a router with some small global data, and an entity cache), or within some sub components on each page.

So I don't need a "single state atom" for anything. What I do need is to manage the state transitions of those complex components. Redux does nothing for me here. XState does tons.

I suspect most people are using redux as a global entity cache, or worse...what you do to store "global" data.

Thread Thread
 
luiz0x29a profile image
Real AI

They talk about Single root of "thruth", but the elephant in the room is the insidious internal mutable state on every React component that's not pure and/or has hooks.

Wasn't the entire point of modelling state/events/transitions knowing when to do actions, like rendering the screen.

So if there was a proper way of doing virtual finite state machines, you wouldn't need react to reconcile the three, you could just go and change the DOM directly with impunity.

The DOM would be the single source of truth for the visual representation of the applications, and it wouldn't be slow because you wouldn't be randomly poking and mutating it without reason.
It would actually be faster, as it would only change precisely in "reaction" to state+events, ironic for something called "React" that doesn't react to anything, it should be called Render.

Collapse
 
jacobweber profile image
JW

This is great....very well-written and clear. I gave up on Redux early because of my confusion about async actions....having to install some kind of third-party middleware just didn't feel right. Looking forward to the second part.

Collapse
 
cdoublev profile image
Guillaume • Edited

Hello David,

I have been reading FSM related posts (including yours) with great interest since a few years now, but I've never taken the step to include this pattern in my projects. I feel like there's more boilerplate and that it's a bit overthinking for simple UI component.

Anyway: "[Transitions] can be represented using boxes and arrows" makes me wonder if you've ever thought about implementing FSM using category theory and algebraic data structures?

Collapse
 
offbeatful profile image
Dennis Miasoutov

Nice article! The moment I saw Redux - I started thinking on building state machines. That resulted in this nice library: github.com/mocoding-software/redux...

It allows build finite state machines using Redux. Please check this out.

Collapse
 
pettermahlen profile image
Petter Måhlén

Interesting article, looking forward to the next part!

Have you seen gist.github.com/andymatuschak/d5f0...?

In github.com/spotify/mobius, which I interpret as similar to the ideas you're describing, we decided to not mandate modelling 'state' as an FSM, though we think that FSMs are sometimes the best model (cf github.com/spotify/mobius/wiki/Def...).

Collapse
 
davidkpiano profile image
David K. 🎹

Hi Petter, yes I've read that a while ago, interesting implementation! Spotify's Mobius (thanks for showing it to me!) also looks interesting, I'll take a deeper look at it.

Collapse
 
jwp profile image
John Peters

This complexity vaporates when we stick to the much simpler event, eventhandler pattern. Events emit data and subscribers handle the notification. Components are small and only do one thing. For example, a Person component works with a Person model and A person object. That's it. This component is responsible to get the person object, subscribe to the result in populate the person model. Later it may contain a FindPerson function which has a well defined input and output interface. The person component grows over time and may provide notifications at any time. Because events are loosely coupled, what happens next is of no concern to this component. Simple concept, used by every browser in the industry why not follow what already works? Event s have been around for 50 years. They are the original observer pattern.

Collapse
 
mrpmorris profile image
Peter Morris

I used NgRx for about a year. I found it unpleasant because I had to write so much boilerplate. When Blazor came out I decided to write a Flux library that took advantage of OOP principles (no need for switch statements, for example).

I came up with Fluxor - no-boilerplate, and UI agnostic. It's far less work.

Collapse
 
_ezell_ profile image
Ezell Frazier • Edited

In layman's terms, the combination of current state and an event decides whether a state-update occurs within a finite state machine?

So we essentially have a table. The columns are possible states, and rows being what can happen based on an event?

Collapse
 
longlho profile image
Long Ho

Have you checked out martinfowler.com/eaaDev/EventSourc... specifically the External Updates section?

Collapse
 
morsmodr profile image
morsmodr • Edited

Can't wait for part 2. I guess one can either make an argument that redux is incomplete or the fact that in large scale applications, redux forces one to write way too much code and makes it easy to cause issues with respect to transitions. We inherently branch out code over time to handle various transitions and FSM approach makes my mind feel at peace.

Regardless, the root cause is with how side effects are handled. And I have loved whatever I have read in this article. Long, but worth the effort. I'm maintaining a large scale application which uses redux-observable to manage side effects and also have to consider state variations across various markets, resulting in a lot of If checks throughout the application. Been looking to implement state machines on top of redux-observable, and I'm not sure that's the right approach. Maybe scratch redux altogether and go all in with state machines and statecharts is another thought, but given its a large app - the thought is scary! Haha.

Again, looking forward to part 2.

Collapse
 
iurijacob profile image
Iuri Jacob

Very nice post. Never had thought in redux as a finite state machine, but it totally makes sense. Enlightening... Waiting for the second part.

Collapse
 
techpeace profile image
Matt Buck

If you're looking for a similar approach for Vue, I'd recommend checking out this post about using Vuex with the xstate library.

Collapse
 
chadsteele profile image
Chad Steele

I'd love it if you wanted to dissect my Redux one line replacement hook... useSync
dev.to/chadsteele/redux-one-liner-...

Collapse
 
farhd profile image
Farhad • Edited

Thank you for sharing your thoughts 👍
You might want to check these out:
sam.js.org
meiosis.js.org
github.com/jeffbski/redux-logic

Collapse
 
luiz0x29a profile image
Real AI

"Water can't be both liquid and gas simultaneously"
Well, it actually can.
thermal-engineering.org/wp-content...

I wonder why no one mentioned it.