1

I have a react component where I am trying to spread objects into the state in the constructor.

constructor() {
    super()

    const shapesArray = [1, 2, 3]

    let renderStates = shapesArray.map((el, i) => {
        return {['shape'+i]: 'black'}
    })

    this.state = { ...renderStates }
    console.log(this.state)
}

I want to access the colors by doing this.state.shape0, but when I console log this.state, I get this:

enter image description here

instead of Object {shape0: "black", shape1: "black", shape2: "black"}.

What am I doing wrong here?

4
  • 1
    You're not using spread syntax in an array literal here??? That should be a syntax error in ES6 (maybe you have enabled some experimental babel stuff)
    – Bergi
    Commented May 13, 2017 at 20:00
  • Btw, for indexed values such as your shape collection, you should always use an object.
    – Bergi
    Commented May 13, 2017 at 20:02
  • 1
    Your renderStates is an array. Shouldn't you use [...renderStates]? Commented May 13, 2017 at 20:10
  • ... is not an operator! Commented May 14, 2017 at 1:23

4 Answers 4

6

That is because you are spreading an Array into an Object. Arrays are actually objects with (usually) sequential integral strings as their keys. These keys are the indices of the array.

As shown below, map takes an array and produces another array

const shapesArray = [1, 2, 3];

const renderStates = shapesArray.map((el, i) => {
  return {
    ['shape' + i]: 'black'
  };
});

console.log(renderStates);

When spreading into an Object, the value of each own enumerable property in the source Object is added to the target under its respective key. Since the keys of an array are its indices you end up with an Object with a property for each element of the Array. The name of each property is its index in the array.

To achieve what you want, you can use Array.prototype.reduce to build an object from the array with the names created in the mapping process.

const shapesArray = [1, 2, 3];

const renderStates = shapesArray
  .map((el, i) => {
    return {
      ['shape' + i]: 'black'
    };
  })
  .reduce((o, element) => {
    Object.keys(element).forEach(key => o[key] = element[key]);
    return o;
  }, {});

console.log(renderStates);

Of course this itself can be written more elegantly by spreading the object inside of reduce.

const shapesArray = [1, 2, 3];

const renderStates = shapesArray
  .map((el, i) => {
    return {
      ['shape' + i]: 'black'
    };
  })
  .reduce((o, element) => ({...o, ...element}), {});

console.log(renderStates);

2
  • Use Object.assign instead of that forEach loop or the nonstandard syntax
    – Bergi
    Commented May 13, 2017 at 22:53
  • Perfect, thanks! I was thinking you could just spread the values of the renderStates array (objects) into an object and their keys would be the indices. Commented May 15, 2017 at 14:52
1

As an optimization to aluan-haddad's answer, reduce can handle the logic that was in map

const shapesArray = [1, 2, 3];

const renderStates = shapesArray
  .reduce((acc, _, i) => ({
    ...acc,
    ['shape' + i]: 'black',
  }), {});

console.log(renderStates);

2
  • Very nice, but you need to specify 3 parameters as the 3rd is bound to the index, the second, to the element, and the first to the accumulator. Note that this looks correct, at a glance, because the array contains sequential numbers, but is actually off by 1. Commented May 17, 2017 at 1:50
  • Thanks @AluanHaddad.
    – SpenserJ
    Commented Jun 10, 2017 at 16:38
0

renderStates is an array which has integer properties starting from 0 or the array indices if you want to be specific, so {...renderStates} will take each index, and create a mapping from this index to the value corresponding to that index, to achieve what you are looking for, you need to reduce your renderStates array to an object like so

let renderStates = shapesArray.map((el, i) => {
        return {['shape'+i]: 'black'}
    }).reduce((resultObj, currentShape,index) => resultObj['shape'+index] = currentShape['shape'+index]), { });

renderStates is now an object, and you can spread it and it will produce the result you want

0

No need to iterate twice over array. Use reduce:

const shapesArray = [1, 2, 3];

const renderStates = shapesArray.reduce((accumulator, i) => {
  accumulator['shape' + i] = 'black';
  return accumulator;
}, {});

console.log(renderStates);

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.