DEV Community

Michael Muscat
Michael Muscat

Posted on • Edited on

Deleting Redux

Redux: A predictable state container for JS apps

A comedy of errors.

Redux is a state management pattern that dates back decades, well before it was popularised in the web era. Like all popular things it has many attractors and detractors. Almost every place I've worked at has used it on at least one project. Whether you like it or not redux is a mainstay of the web as we know it today. If you've used redux before, you'll know it comes with an ungodly amount of boilerplate, indirection, and confusion. Try not to peel your eyes away as you look at this *simple* example, something you might find in a tutorial. ```tsx const Increment = createAction("Increment", props<{ by: number }>()) const Double = createAction("Double") class UICounterEffects { double = createEffect((actions) => { return actions.pipe( ofType(Increment), map(action => new Double()), delay(2000), ) }) } function countReducer(state, action) { switch(action.type) { case Increment.type: { return state += action.by } case Double.type: { return state *= 2 } } } const countSelector = createSelector(state => state.count) const initialState = { count: 0 } const REDUCERS = { count: countReducer } const Store = createStore(REDUCERS, { initialState, effects: [UICounterEffects] }) @Component({ providers: [Store], template: {{ count | async }} Increment }) export class UICounter { private store = inject(Store) count = this.store.select(countSelector) increment(by) { this.store.dispatch(new Increment({ by })) } } ``` Holy over-engineering Batman! So much code for so little action. Let's see what it looks like *without* redux. ```tsx @Component({ template: {{ count }} Increment }) export class UICounter { // we initialize some state count = 0 // we dispatch an action increment(by) { // we update the state this.count += by // we run some effects setTimeout(() => { // we update the state again this.count *= 2 }, 2000) } } ``` The first example is **1136 bytes** of code across **56 lines**. The second example does exactly the same thing in only **301 bytes** across **18 lines**! It is also easier to read and understand by an order of magnitude.
Clearly something is wrong with people who use redux.

What about the "Dev tools", "time travel" and "immutable state"? I say these are highly overrated and redundant. Do the next guy a favour, shift+delete redux from your application.

State should be sliced according to function (eg. authentication state, user state, form state). Components should not rely on contextual (read: global) state unless that state really needs to be shared across your application. In short, just use services.

Even if you can't delete redux from your application, that's fine. Just stop using it for everything.

@Component({
   template:
      <div>{{ count }}</div>
      <button (click)="increment(1)">
         Increment
      </button>
})
export class UICounter {
   // we initialize some state
   count = 0
   // we dispatch an action
   @Action() increment(by) {
      // we update the state
      this.count += by
      // we run some effects
      return dispatch(timer(2000), () => {
         // we update the state again
         this.count *= 2
      })
   }
}

Enter fullscreen mode Exit fullscreen mode
An acceptable amount of boilerplate

Top comments (0)