2

I have a 2D array of objects with key/value pairs as a state variable, and I'm trying to use the recommended way of setting/changing state variables, which is to use this.setState({x:y}) instead of directly setting it using this.state.x = y and then forceUpdate(). However, when I try to do that, it gives me an "unexpected token" error.

I basically want to flip a variable from one state to the other, so I'm usng a ternary operator. This code works

toggleBookmark(category, index) {   
    this.state.menuItems[category][index].bmIcon = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
}

This code, which I'd expect to do the same thing, gives an error

toggleBookmark(category, index) {   
    this.setState({menuItems[category][index].bmIcon: (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o"});
}

I thought it might be the ternary operator, so I put the value into a variable and tried setting the state variable with that, but it still gives the same error.

toggleBookmark(category, index) {
    var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
    this.setState({menuItems[category][index].bmIcon: iconText});
}

Am I doing something wrong? Is what I want to do possible with setState()?

Pedram
  • 7,479
  • 6
  • 25
  • 25

2 Answers2

3

In Javascript, you cannot use an expression as a key for an object when creating that object inline.

The problem here is that you have done {menuItems[category][index].bmIcon: iconText} which will throw a syntax error.

If you want a quick way to solve this, you may create the object first, then assign the value to that key like this:

var state = {}; state[menuItems[category][index].bmIcon] = iconText; this.setState(state);

It's worth noting however that ES6 Provides a sugar for doing this, and there is another answer here that might provide more insight How do I create a dynamic key to be added to a JavaScript object variable

Update:

I now see what you meant, I had previously assumed that menuItems already defined, but what you want to do is change the value of a key inside a nested object that is in this.state

This is something that React is not really built to do, you should keep your state relatively simple, and make separate React components for each menu item, then have them manage their own state. I would strongly recommend this approach because it will keep your code clean and robust. Don't be afraid to make more components!

However if you do want to keep all this nested state in one component (not advised), then you should first make a copy of the object you want to setState on.

var newMenuItems = _.clone(this.state.menuItems);
var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
newMenuItems[category][index].bmIcon = iconText;

this.setState({ menuItems: newMenuItems });

OR

var iconText = (this.state.menuItems[category][index].bmIcon === "bookmark-o") ? "bookmark" : "bookmark-o";
this.state.menuItems[category][index].bmIcon = iconText;

this.forceUpdate();

(First method preferred, but it requires you have something like underscore or lodash installed )

Community
  • 1
  • 1
Braden
  • 1,548
  • 2
  • 12
  • 20
  • This didn't seem to work, unfortunately. Following the suggestion in the link to the other question you provided, I tried putting square brackets around the expression, so it looked like `this.setState({[menuItems[category][index].bmIcon]: iconText})`, but then it gave a "menuItems not defined" error. Indeed, the code you provided gave the same error. – Pedram Sep 02 '15 at 06:31
  • To resolve the error I put `this.state.` in front of menuItems, but even though that makes the error go away, the value inside the state variable array doesn't actually get updated (with either the square bracket method or the way you provided). Perhaps React Native's setState() method doesn't like this way of referencing state variables? – Pedram Sep 02 '15 at 06:32
  • I used your suggested method of making a new component to keep track of its icon (in addition to other things for each menu item). The performance is better that way too. Thanks! – Pedram Sep 03 '15 at 06:17
0

I have the data chat:

chat: {
        id: 'ss3k5e6j1-6shhd6-sdasd3d3-23d5-gh67',
        agentName: 'egaliciar',
        agentAvatar: 'http://i.imgur.com/DY6gND0.png',
        messages: [
          {
            id: 1,
            lines: [
              'Me pueden ayudar?',
              'Tengo problemas con mis boletos',
              'Hola buen dia...',
            ],
            time: '17:20',
          },
          {
            id: 2,
            lines: ['¿Me podria regalar su nombres', 'Con gusto...'],
            time: '17:22',
            date: '23/ene/2012',
          },
        ],
      },
    };

and when i do

const oldLines =Object.assign({}, this.state.chat);
    oldLines.messages[0].lines.push('newValue');

My state Changed..... without this.setState({});

I Made a Clone;

var clone = JSON.parse(JSON.stringify(this.state.chat)); 
        clone.messages[0].lines.push('new Value');

and the State maintain their state; thus, the complete solution is for me:

var clone = JSON.parse(JSON.stringify(this.state.chat)); 
        clone.messages[0].lines.push(questionAreaMessage); //the state maintains
        this.setState({chat:clone}); //here the State change!!!!
Egalicia
  • 683
  • 9
  • 17