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