DEV Community

Discussion on: Redux is Dead: Long Live Redux Toolkit

Collapse
 
captaincodeman profile image
Simon Green • Edited

It's a pity that the solution to Redux complexity is to add yet more JS code, rather than to give it the major refactor it really needs. IMO it suffers from over-use of currying for configuration, which is "clever" but makes learning and using it way more difficult than it needs to be. The definition of actions and reducers is repetitive and complex with code for a single thing spread out in multiple locations which again, in my experience, makes it really difficult for people to learn.

Although I loved what Redux ultimately provided (predictable state), the way it went about it was so convoluted it made working with it a chore. Tools like Rematch mask some of the issues but the fundamental problems can't be solved with wallpaper.

I had a go at creating something similar to Rematch, which was based on the same premise (reversing the store definition to make things easier) but done as a complete re-write rather than a veneer on top, and aiming to provide full strict Typescript support:

captaincodeman.github.io/rdx

Collapse
 
markerikson profile image
Mark Erikson

What specific concerns do you have?

The only uses of "currying" I can think of are writing middleware, which is a very rare use case, and the connect API, which has basically been replaced by useSelector.

Looking at your linked lib, you show this example:

import { createModel } from '@captaincodeman/rdx'

export const counter = createModel({
  state: 0,
  reducers: {
    add(state, value: number) {
      return state + number
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

RTK's createSlice is effectively identical to that:

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    add(state, action) {
      return state + action.payload;
    }
  }
})
Enter fullscreen mode Exit fullscreen mode

and RTK is written in TS and designed for a great TS usage experience:

redux.js.org/tutorials/typescript-...

The only immediate difference I see is that RTK's action creators and reducers are still standalone functions, rather than being attached to a store instance.

Collapse
 
brianmcbride profile image
Brian McBride

Why do we need to use this action/payload and then switch on the action type?
We are going to write code in those switch blocks. Why are we not just writing a function: doMyAction(payload) ?

I totally agree with what you are saying. I don't see the need for Context as we have JS modules. import works really well. There are plenty of more maintainable patterns; singleton, factory, etc...

When I see the whole { action: 'ACTION', payload: somePayload } I think of the rare times I use some broadcast tooling. Event emitting can be fun, but over time it, too, ends up being complexity that is rarely needed.

I understand wanting predictable state 10000%. How my teams maintain that - unit tests. I'll stop this over-reply to emphasis that for newer developers. When it comes to your state and business logic, write unit tests. Get as close to 100% coverage as you can. I'm more lax about testing UI components (it is still good), but whatever state code you use - test the shit out of it.

PS. Simon, always love your thoughts. I remember how prolifiic and helpful you were on the Polymer slack channel. I still wait for WebComponets to just replace React components. LIT? Maybe?

Collapse
 
markerikson profile image
Mark Erikson

Why are we not just writing a function: doMyAction(payload) ?

Because "just functions" does not provide the properties needed to enable Redux's design goals, including serializing a history log of "all the things that happened in the app" and "allowing middleware to modify actions".

See the list of goals under The Tao of Redux, Part 1 - Implementation and Intent: The Intent and Design of Redux.

Thread Thread
 
brianmcbride profile image
Brian McBride

I'm not a huge fan of middleware either. It is sort of dispatch -> mystery transformation -> reducer. Entering a codebase with a lot of middleware can be super confusing.

This is not elegant, but an example myMethod(aTransformer(payload)) another developer coming in knows exactly what is going on. The downside is that you will be typing in that transformer over and over. If the middleware is fetching data, well. fetchFn(payload).then(myFunction)

I also totally get the idea of serializing events. I have built many API services that do something similar. I often use something like jsonpatch.com/ where I have a data object that I can apply, unwind, flatten, etc... It is pretty easy to have a function getPatch(prevState, nextState) then drop that in a Set/Array/Database/etc...

I'd argue that something like a JSON Patch becomes more useful anywhere. What you define as { action: "someAction", payload: "somePayload" } is not always a straight up merge. Often the payload is a single key in the state. Or there is business logic in the reducer. Thus, you need to know how the code works and if you change the the code, you break all previously stored actions if you needed to reapply them.

Of course, I'm talking about actually storing event history. In reality, how many React develpers do much more than a simple undo and even then I rarely see that. What I do see is that developers use the actions in Redux tooling to see if their event fired and what side effects might have happened. And right there I would say build that all in a Jest test and check your results there with assertions. Like, actually develop in your test runner. Then you have test coverage and you'll know if your state breaks with some code/dep update.

Collapse
 
antonmelnyk profile image
Anton Melnyk • Edited

with code for a single thing spread out in multiple locations which again, in my experience, makes it really difficult for people to learn.

And that's the whole point. Redux is about separating what happend from how that changes the state. Without separation there is no point to use Redux at all except maybe for cool dev tools.