Search code examples
javascriptreactjstemplate-literals

Using template literal to dynamically add values to setState in React without eval()


I'm new at React and trying to create a scoreboard app which has the following state:

state = {
    home_team: [
      {
        id: 1,
        name: "Jobe Watson",
        goals: 0,
        behinds: 0,
      },
      {
        id: 2,
        name: "James Hird",
        goals: 0,
        behinds: 0,
      },
      {
        id: 3,
        name: "Steven Alessio",
        goals: 0,
        behinds: 0,
      },
      {
        id: 4,
        name: "Che Cockatoo-Collins",
        goals: 0,
        behinds: 0,
      }
    ],
    away_team: [
      {
        id: 1,
        name: "Chris Judd",
        goals: 0,
        behinds: 0,
      },
      {
        id: 2,
        name: "Anthony Koudafidis",
        goals: 0,
        behinds: 0,
      },
      {
        id: 3,
        name: "Steven Silvagni",
        goals: 0,
        behinds: 0,
      },
      {
        id: 4,
        name: "Brendan Fevola",
        goals: 0,
        behinds: 0,
      },
    ]
  }

I am trying to run a score change function as below:

addScore = (i, delta, team, score) => {
    const val = eval(`this.state.${team}[${i}].${score} += ${delta}`)
    console.log(val)
    this.setState( {
        [score]: val
    })
  }

where it is called in the Counter component as follows:

onClick={()=>addScore(index, +1, team, 'goals')

In this case, "index" refers to the player index in either team array, and "team" refers to either "home_team" or "away_team".

The only way I seem to be able to dynamically add the information into the setState method seems to be through a template literal into eval() then calling it in setState.

Knowing there are problems with eval() - are there any other ways to do this? I have tried using new Function()() without success.

Can anyone offer another solution? Thanks in advance.


Solution

  • I think you are bit confused on how to get access value via all available variables, here you go simple way :

    // this.state.${team}[${i}].${score}
    
    this.state[team][i][score]
    

    Second you are mutating state, that you shouldn't, you can update state this like :

    addScore = (i, delta, team, score) => {
        const updated = this.state[team].map((el,index) => {
            if(i=== index) {
                const score = el.score + delta;
                el = { ...el , score }
            }
            return el
        })
    
        this.setState((state) => {
            ...state ,
            [team] : updated
        })
    }