0

I need to update a slice of a slice of my state, without updating the entire slice; only the sub-slice. I've seen others combine reducers to accomplish this, but I haven't figured out how to do this within my project yet. Redux.js.org shows this as another way to do it:

function updateVeryNestedField(state, action) {
    return {
        ....state,
        first : {
            ...state.first,
            second : {
                ...state.first.second,
                [action.someId] : {
                    ...state.first.second[action.someId],
                    fourth : action.someValue
                }
            }
        }
    }
}

Unfortunately, my project is in javascript. How can I replicate this with vanilla javascript without the spread operator? I've tried some variations using Object.assign but couldn't get it to work.

skwny
  • 2,930
  • 4
  • 25
  • 45

1 Answers1

1

As you mentioned, you can use Object.assign in place of spread:

{...state, x: 1}

should give you the same result as

Object.assign({}, state, {x: 1})

Note the empty object as the first parameter of Object.assign. What this is doing is copying the values from state into a new empty object, then copying the values from {first: 1} into the new object.

For example:

var a = { x: 1, y: 2}
var b = {...a, y: 3 }
console.log(a); // { x: 1, y: 2 }
console.log(b); // { x: 1, y: 3 }

is equivalent to

var c = { x: 1, y: 2 }
var d = Object.assign({}, a, { y: 3 });
console.log(c); // { x: 1, y: 2 }
console.log(d); // { x: 1, y: 3 }

The important part is that both a and c are unchanged. b and d have a new value for y.

For nested objects, you repeat this process:

function updateVeryNestedField(state, action) {
    return Object.assign({}, state, {
        first : Object.assign({}, state.first, {
            second : Object.assign({}, state.first.second, {
                [action.someId] : Object.assign({},
                    state.first.second[action.someId],
                    { fourth : action.someValue }
                )
            })
        })
    });
}

var oldState = { first: { second: { 'anId': { fourth: 3 } } } };
var newState = updateVeryNestedField(oldState, { someId: 'anId', someValue: 5 });
console.log(oldState.first.second.anId.fourth); // 3
console.log(newState.first.second.anId.fourth); // 5

However, for full ES2015 support you also won't be able to use computed keys. So instead you'd have build some of the object before the return statement. For example:

function updateVeryNestedField(state, action) {
    // Create a temporary object to avoid use of computed keys
    var updatedSecond = Object.assign({}, state.first.second);
    updatedSecond[action.someId] = Object.assign({}, state.first.second[action.someId], {
        fourth: action.someValue
    });
    return Object.assign({}, state, {
        first : Object.assign({}, state.first, { second : updatedSecond })
    });
}

var oldState = { first: { second: { 'anId': { fourth: 3 } } } };
var newState = updateVeryNestedField(oldState, { someId: 'anId', someValue: 5 });
console.log(oldState.first.second.anId.fourth); // 3
console.log(newState.first.second.anId.fourth); // 5
Community
  • 1
  • 1
grovesNL
  • 6,016
  • 2
  • 20
  • 32
  • Is this allowable in es5: `[action.someId] : ...`? It is throwing an error when minifying with uglifier.js, and others have also experienced this issue. However, it passes when bundling but not minifying. – skwny May 07 '17 at 17:52
  • @skwy Unfortunately computed keys are part of ES2015. I've updated my example to show how to avoid using computed keys by introducing a temporary object. The new example uses indexing to add a property to the temporary object. – grovesNL May 07 '17 at 19:30
  • Thank you once again.. What's strange is that it passes the bundling but not the minification. I'm using Browserify. Perhaps transforming computed keys is built into the bundling process for Browserify? – skwny May 07 '17 at 22:40