3

I'm working with a reducer using the React / Redux architecture. It's not specifically relevant to my question, as this is more of an ES2015 question. I have the following code:

return objectAssign({}, state, {
        sendingRequest: false,
        currentModel: {
            ...state.currentModel,
            components: [
                ...state.currentModel.components,
                0: {
                    ...state.currentModel.components[0], // can't do this, why?
                    selectedComponentGroup: {
                        ...state.currentModel.components[0].selectedComponentGroup,
                        availableComponents: action.payload.data
                    }
                }
            ]
        }
    });

However, ESLint is throwing an error:

✘  http://eslint.org/docs/rules/  Parsing error: Unexpected token  
C:\...\ProjectModelsReducer.js:122:25
...state.currentModel.components[action.componentIndex],

It's complaining specifically about the spread operator for this line:

...state.currentModel.components[action.componentIndex]

I can't figure out why this doesn't work and I was wondering if someone could shed some light into why the spread operator can't be used here.

po3t
  • 148
  • 1
  • 9
  • 1
    Sounds like you've got experimental `stage-x` stuff enabled for Babel, but don't have ESLint configured to parse using Babel. Spreading objects is not standard ECMAScript. – loganfsmyth May 10 '17 at 23:15
  • 1
    You cannot have `0: ` inside an array literal. – Bergi May 10 '17 at 23:21
  • Oh good call, missed that the spread there was an array and not an object, so this is a syntax error in general, not just ESLint. – loganfsmyth May 10 '17 at 23:22
  • @Bergi Why is that? How would I change the contents of the object that's at array index 0? – po3t May 10 '17 at 23:29
  • Like this? `[state.currentModel.components[0]]:` – po3t May 10 '17 at 23:33
  • 1
    [`...` is not an operator!](https://stackoverflow.com/questions/37151966/what-is-spreadelement-in-ecmascript-documentation-is-it-the-same-as-spread-oper/37152508#37152508) – Felix Kling May 11 '17 at 00:07
  • Okay, technically you're correct, but it is the umbrella term that is used throughout the industry. [Microsoft](https://learn.microsoft.com/en-us/scripting/javascript/reference/spread-operator-decrement-dot-dot-dot-javascript) and the [Official Redux Docs](http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html) both use the term "spread operator". – po3t May 12 '17 at 03:33

2 Answers2

3
components: [
    ...state.currentModel.components,
    0: {
        // ...
    }
]

is not valid, just like [0: {}] is not valid as an array declaration. If you want to create a new array with a specific item changed, you'd have to use spread along with normal array syntax, or clone the array and mutate the index later, e.g.

components: [
    {
        // ...
    },
    ...state.currentModel.components.slice(1),
]

to create a new array with the first item replaced, or do

components: Object.assign([...state.currentModel.components], {
    0: {
        // ...
    }
}),

to clone the array then change the item at the 0 index.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • Yeah, that was where I went wrong. The declarative nature of the Redux reducers screwed me up. Long day of looking at code. Needed some extra eyes :). Your second example is essentially what I was missing. Thanks! – po3t May 10 '17 at 23:55
  • Is there a place where I can learn this? (spread operator object beyond the basics, with examples) Every tutorial I see shows the same exact thing – user3808307 Dec 04 '17 at 03:36
0

I see my mistake now. I was treating the array like an object and using the index as a key in the array, but the array doesn't use keys when they are being defined. I have to do something like this:

components: [
    ...state.currentModel.components,
    {
        0: {
            ...state.currentModel.components[0],
            selectedComponentGroup: {
                ...state.currentModel.components[0].selectedComponentGroup,
                availableComponents: action.payload.data
            }
        }
    }
]

Ultimately I ended up using the immutability-helper to make the update more clean.

components: update(state.currentModel.components, {
    0: {
        $merge: {
            selectedComponentGroup: {
                ...state.currentModel.components[0].selectedComponentGroup,
                availableComponents: action.payload.data
            }
        }
    }
})
po3t
  • 148
  • 1
  • 9