Reputation: 566
I am learning Redux. I am kind of new so I dont want to develop a bad habit at the start which is why i am writing this.
The doc i am reading says that in the reducers we need to always set the state where we change it - in other words do the following:
const someReducer = (state=initState, {type, payload}) => {
switch(type) {
case 'CHANGE_VALUE_ONE' : {
state = {...state, valueOne: payload }
break
}
case 'CHANGE_VALUE_TWO' : {
state = {...state, valueTwo: payload }
break
}
default:
state = state
}
return state
}
My approach was creating a newState constant which is identical to the state we receive, make change to the dummy and return it - or in code:
const userReducer = (state= {name: null, age: null}, {type, payload}) => {
const newState = {...state}
switch(type) {
case 'CHANGE_VALUE_ONE' : {
newState.valueOne= payload
break
}
case 'CHANGE_VALUE_TWO' : {
newState.valueTwo= payload
break
}
}
return newState
}
I think the way i do it is cleaner - if i have a complicated state i can easily change what i need to change without mutating the original state.
Upvotes: 3
Views: 239
Reputation: 98
Your solution isn't immutable, the doc's one is. In more complex cases, I recommend to you use immer, the lib approach is similar to yours, but you will mutate a copy of the state and then immer
will merge this copy with the actual state to create a new one. Helps you to work with nested data and bring immutability to reducers. Your case, with immer:
const userReducer = produce(state, draft => {
switch(type) {
case 'CHANGE_VALUE_ONE' : {
draft.valueOne= payload
break
}
case 'CHANGE_VALUE_TWO' : {
draft.valueTwo= payload
break
}
}
},{name: null, age: null})
Upvotes: 1
Reputation: 322
Always keep the state immutable, use pure function. And there is a performance issue for your approach.
Your approach is immutable. But remember, any dispatched action will go through all reducers. That means even the action is not related to your reducer, you still create a new shallow copy of it. It's not necessary. Don't do this
// This newState will be created by every action
const newState = {...state}
switch(type) {
case 'CHANGE_VALUE_ONE' : {
newState.valueOne= payload
break
}
case 'CHANGE_VALUE_TWO' : {
newState.valueTwo= payload
break
}
}
return newState
Upvotes: 1
Reputation: 104539
With first snippet, you are violating the principle of pure reducer. That means instead of updating the value directly in state, create a new copy of it and do the changes in that.
As per Doc:
Reducers are just pure functions that take the previous state and an action, and return the next state. Remember to return new state objects, instead of mutating the previous state.
Which you are clearly violating by assigning new value to state
, here:
state = {...state, valueOne: payload }
Note: Don't forget const newState = {...state}
, will only create the shallow copy, so if you have nested state, this will also violate the principle.
In case of nested state, update it in this way:
var obj = { car: { model: '', num: '' } };
function update() {
return {
...obj,
car: {
...obj.car,
model: 'abc'
}
}
}
console.log('new obj = ', update());
console.log('old obj = ', obj);
Upvotes: 8
Reputation:
I think you overcomplicate yourself. Here's a reducer that I use (as an example):
import * as types from '../actions/types';
import { Enums } from '../helpers/enums';
const initialState = {
testState: Enums.command.STOP
};
export default function(state = initialState, action) {
switch (action.type) {
case types.SET_CREEP_TEST_STATE:
return {...state, testState: action.testState};
case types.SET_CREEP_TEST_TIME:
return {...state, testime: action.testime};
default: return state;
}
};
It shouldn't be any more complex than that.
Upvotes: 2