For the time being I can't use ES6/ES2015 and I'm stuck with ES5 for writing Redux reducers. Since the state parameter of a reducer has to be immutable, even when it's undefined, I came up with the following pattern:
function myState( state, action ) {
if ( typeof state === 'undefined' ) {
return myState( { value1: 'foo', value2: 'bar' }, action );
}
// actual state calculation here
}
Any alternative suggestions or comments on how to ensure a default value with ES5?
Edit: After some questions and suggestions: the reason I do a recursive call is that I take the "state is immutable" very seriously. So even when the state
parameter is undefined
, I don't change the parameter variable itself. Am I taking the immutability too far?
Redux doesn’t force you to use the default argument syntax. It only cares that when it gives you undefined
as the state, you return something else so that your app is able to boot up with an initial state tree.
This function in ES6:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state + 1
default:
return state
}
}
Is equivalent to this function in ES5:
function counter(state, action) {
if (state === undefined) {
state = 0
}
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state + 1
default:
return state
}
}
A nice way to verify this is to run this code through the Babel REPL.
The reason I do a recursive call is that I take the "state is immutable" very seriously. So even when the state parameter is undefined, I don't change the parameter variable itself.
There is no need for a recursive call here. I think your question may contain some confusion about the difference between mutation and reference assignment.
When you write
var x = { lol: true }
x.lol = false
you are mutating the x
object. This is what Redux doesn’t allow.
However when you write
var x = { lol: true }
x = { lol: false }
the original object stays intact. x
“binding” (also known as a “variable”) just starts pointing at a different object.
Redux doesn’t care if you change what state
argument refers to. It is local to your function. Whether you return it or not, changing the reference fine as long as you don’t mutate the actual objects or any objects inside it.
Just changing what the variable refers to does not mutate the objects:
// good: local variable called "state" refers to a different number
state = state + 1
// good: local variable called "state" refers to a different array
state = state.concat([42])
// good: local variable called "state" refers to a different string
state = state + ", lol"
However changing something inside the object itself, or objects it links to, whether deeply or not, is a mutation, and is not allowed by Redux:
// bad: object that local variable "state" refers to has been mutated
state.counter = state.counter + 1
// bad: object that local variable "state" refers to has been mutated
var sameObjectAsState = state
state.counter = state.counter + 1
// bad: array that local variable "state" refers to has been mutated
state.push(42)
// bad: array that local variable "state" refers to has been mutated
var sameArrayAsState = state
sameArrayAsState.push(42)
// bad: object that is linked from the object that local variable "state" refers to has been mutated
state.something.deep.counter = 42
// bad: object that is linked from the object that local variable "state" refers to has been mutated
var somethingDeep = state.something.deep
somethingDeep.counter = 42
Another option would be something like this:
var initialState = { value1: 'foo', value2: 'bar' };
function myState( state, action ) {
state = state || initialState;
// actual state calculation here
}