Chris Chris - 2 months ago 13
Javascript Question

How to Update specific slice of redux state in React

I seem to have hit a snag when updating state using redux and react-redux. When I update an individual slice of state, all of the others get removed. I know the answer to this will be simple but I can't figure it out and haven't found anything else online.

So to clarify, here's my reducer:

const initialState = {
selectedLevel: null,
selectedVenue: null,
selectedUnitNumber: null,
selectedUnitName: null,
selectedYear: null
}

export default (state = initialState, action) => {
console.log('reducer: ', action);
switch (action.type){
case 'CHOOSE_LEVEL':
return action.payload;
case 'CHOOSE_VENUE':
return action.payload;
case 'CHOOSE_UNIT':
return action.payload;
case 'SHOW_COURSES':
return action.payload;
}

return state;
}


And my combine reducer:

export default combineReducers({
workshopSelection: WorkshopSelectReducer
});


So my initial state looks like this:

workshopSelection: {
selectedLevel: null,
selectedVenue: null,
selectedUnitNumber: null,
selectedUnitName: null,
selectedYear: null
}


But when I use one of my action creators, for example:

export function chooseVenue(venue){
return {
type: 'CHOOSE_VENUE',
payload: {
selectedVenue: venue
}
}
}


I end up with state looking like this:

workshopSelection: {
selectedVenue: 'London',
}


All of the rest of the state within this object that wasn't affected by this action creator has been completely wiped out. Instead, I just want all other entries to stay as they are with their original values -
null
in this example, or whatever other value has been assigned to them.

Hope that all makes sense.

Cheers!

Answer

You are basically replacing one object (previous state) with another one (your payload, which is also an object).

In terms of standard JS, this would be the equlivalent of what your reducer does:

var action = {
    type: 'CHOOSE_VENUE',
    payload: {
        selectedVenue: venue
    }
};

var state = action.payload;

The simplest way to fix this would be using Object spread properties:

export default (state = initialState, action) => {
    switch (action.type){
        case 'CHOOSE_LEVEL':
        case 'CHOOSE_VENUE':
        case 'CHOOSE_UNIT':
        case 'SHOW_COURSES':
            // Watch out, fall-through used here
            return {
                ...state,

                ...action.payload
            };
    }

    return state;
}

... but since this is still in experimental phase, you have to use some other way to clone previous properties and then override the new ones. A double for ... in loop could be a simple one:

export default (state = initialState, action) => {
    switch (action.type){
        case 'CHOOSE_LEVEL':
        case 'CHOOSE_VENUE':
        case 'CHOOSE_UNIT':
        case 'SHOW_COURSES':
            // Watch out, fall-through used here
            const newState = {};

            // Note: No key-checks in this example
            for (let key in state) {
                newState[key] = state[key];
            }
            for (let key in action.payload) {
                newState[key] = action.payload[key];
            }

            return newState;
    }

    return state;
}
Comments