Deleting Redux
Michael Muscat
Posted on August 27, 2022
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.
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
})
}
}
💖 💪 🙅 🚩
Michael Muscat
Posted on August 27, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.