The redux documentation, when recommending best practices for reducer, mentions refactoring away switch statements and replacing them with a dictionary/map of action to handler, so instead of:
switch(action) {
case 'action1':
doAction1(payload);
case 'action2':
doActions2(payload);
}
you'd have something like:
var handlers = {
'action1': doAction1,
'action2': doAction2,
}
handlers[action](payload);
(see https://redux.js.org/usage/structuring-reducers/refactoring-reducer-example)
I can see that the dictionary approach is cleaner to read. I am wondering if this is the only reason it is preferred? Or does the dictionary outperform the switch too?
Some people have ideological objections to switch statements, partly because of the possibility of missing break
statements accidentally and having fall-through cases. In the case of a redux reducer, that's not an issue since each case returns the new state, so I personally have no problem with the switch here, and I'm not even sure I agree the dictionary is any 'cleaner'.
WRT performance, I would go with the stylistically preferred option because in 99.9% of redux cases, you won't be updating state frequently enough for such minor differences to matter. But OK, let's suck it and see which performs faster:
const keys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
let count = 0;
const increment = () => ++count; // just do something on each action
console.time("switch");
for (let i = 0; i < 10000000; i++) {
const k = keys[Math.floor(Math.random() * keys.length)];
switch (k) {
case 'a':
increment();
break;
case 'b':
increment();
break;
case 'c':
increment();
break;
case 'd':
increment();
break;
case 'e':
increment();
break;
case 'f':
increment();
break;
case 'g':
increment();
break;
case 'h':
increment();
break;
case 'i':
increment();
break;
case 'j':
increment();
break;
case 'k':
increment();
break;
case 'l':
increment();
break;
case 'm':
increment();
break;
case 'n':
increment();
break;
case 'o':
increment();
break;
case 'p':
increment();
break;
case 'q':
increment();
break;
case 'r':
increment();
break;
case 's':
increment();
break;
case 't':
increment();
break;
case 'u':
increment();
break;
case 'v':
increment();
break;
case 'w':
increment();
break;
case 'x':
increment();
break;
case 'y':
increment();
break;
case 'z':
increment();
break;
}
}
console.timeEnd('switch');
console.time('map');
const map = keys.reduce((a, b) => {
a[b] = increment;
return a;
}, {});
for (let i = 0; i < 10000000; i++) {
const k = keys[Math.floor(Math.random() * keys.length)];
map[k]();
}
console.timeEnd('map');
// switch: 529.752ms
// map: 737.213ms
So switch wins, by a significant, though not enormous margin. However, you'll never notice it in practice, so go with the one you prefer from a readability perspective.