最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Toggle item in an array react - Stack Overflow

programmeradmin6浏览0评论

I want to toggle a property of an object in an array. The array looks as follows. This is being used in a react ponent and When a user clicks on a button I want to toggle the winner.

const initialFixtures = [{
    teams: {
      home: 'Liverpool',
      away: 'Manchester Utd'
    },
    winner: 'Liverpool'
  },
  {
    teams: {
      home: 'Chelsea',
      away: 'Fulham'
    },
    winner: 'Fulham'
  }, ,
  {
    teams: {
      home: 'Arsenal',
      away: 'Tottenham'
    },
    winner: 'Arsenal'
  }
];

My react code looks something like this

function Parent = () => {

  const [fixtures, setUpdateFixtures] = useState(initialFixtures)
  const toggleWinner = (index) => {
     const updatedFixtures = fixtures.map((fixture, i) => {
           if (i === index) {
                return {
                    ...fixture,
                    winner: fixture.winner === home ? away : home,
                };
            } else {
                return fixture;
            }
     }) 
     setUpdateFixtures(updatedFixtures);
  }

  return <Fixtures fixtures={fixtures} toggleWinner={toggleWinner} />;

}


function Fixtures = ({ fixtures, toggleWinner }) => { 
  fixtures.map((fixture, index) => ( 
    <div>
        <p>{fixture.winner} </p>
    <button onClick = {() => toggleWinner(index)}> Change Winner</button> 
    </div>
  ))
}

the code works but it feels like it is a bit too much. I am sure there is a better more succinct way of doing this. Can anyone advise? I do need to pass the fixtures in from the parent of the Fixture ponent for architectural reasons.

I want to toggle a property of an object in an array. The array looks as follows. This is being used in a react ponent and When a user clicks on a button I want to toggle the winner.

const initialFixtures = [{
    teams: {
      home: 'Liverpool',
      away: 'Manchester Utd'
    },
    winner: 'Liverpool'
  },
  {
    teams: {
      home: 'Chelsea',
      away: 'Fulham'
    },
    winner: 'Fulham'
  }, ,
  {
    teams: {
      home: 'Arsenal',
      away: 'Tottenham'
    },
    winner: 'Arsenal'
  }
];

My react code looks something like this

function Parent = () => {

  const [fixtures, setUpdateFixtures] = useState(initialFixtures)
  const toggleWinner = (index) => {
     const updatedFixtures = fixtures.map((fixture, i) => {
           if (i === index) {
                return {
                    ...fixture,
                    winner: fixture.winner === home ? away : home,
                };
            } else {
                return fixture;
            }
     }) 
     setUpdateFixtures(updatedFixtures);
  }

  return <Fixtures fixtures={fixtures} toggleWinner={toggleWinner} />;

}


function Fixtures = ({ fixtures, toggleWinner }) => { 
  fixtures.map((fixture, index) => ( 
    <div>
        <p>{fixture.winner} </p>
    <button onClick = {() => toggleWinner(index)}> Change Winner</button> 
    </div>
  ))
}

the code works but it feels like it is a bit too much. I am sure there is a better more succinct way of doing this. Can anyone advise? I do need to pass the fixtures in from the parent of the Fixture ponent for architectural reasons.

Share Improve this question edited Jan 23, 2019 at 10:05 peter flanagan asked Jan 23, 2019 at 9:47 peter flanaganpeter flanagan 9,85027 gold badges82 silver badges140 bronze badges 5
  • Where's changeWinner function? – frogatto Commented Jan 23, 2019 at 9:55
  • Where and the home and away variables declared ? – Treycos Commented Jan 23, 2019 at 9:57
  • the code works but it feels like it is a bit too much. Code review requests are off-topic here. Look at CR – hindmost Commented Jan 23, 2019 at 9:59
  • 1 Quick question. How is this: function Parent = () => { working?? – Rajesh Commented Jan 23, 2019 at 10:08
  • it is a functional ponent being rendered in App. It is using Hooks if that is what you are asking? – peter flanagan Commented Jan 23, 2019 at 10:36
Add a ment  | 

6 Answers 6

Reset to default 4
const updatedFixtures = [...fixtures];
const fixture = updatedFixtures[i];
updatedFixtures[i] = {
  ...fixture,
  winner: fixture.winner === fixture.teams.home ? fixture.teams.away : fixture.teams.home,
};

You can slice the fixtures array into three parts:

  • from 0 to index: fixtures.slice(0, index). This part is moved to the new array intact.

  • The single item at index. This part/item is thrown away because of being changed and a new item is substituted.

  • The rest of the array: fixtures.slice(index + 1).

Next, put them into a new array:

const newFixtures = [
    ...fixtures.slice(0, index),    // part 1
    {/* new item at 'index' */},    // part 2
    ...fixtures.slice(index + 1)    // part 3
];

To construct the new item:

  • Using spread operator:

    const newFixture = {
        ...oldFixture,
        winner: /* new value */
    };
    
  • Using Object.assign:

    const newFixture = Object.assign({}, oldFixture, {
        winner: /* new value */
    });
    

if you write your code in such a way - this will do the job.

const toggleWinner = index => {
    const { winner, teams: { home, away } } = fixtures[index];
    fixtures[index].winner = winner === home ? away : home;

    setUpdateFixtures([...fixtures]);
};

Setting a new array of fixtures to state is pletely enough to trigger render on Fixtures ponent.

I have made a working example for you.

You can use libraries like immer to update nested states easily.

const initialFixtures = [{
    teams: {
      home: 'Liverpool',
      away: 'Manchester Utd'
    },
    winner: 'Liverpool'
  },
  {
    teams: {
      home: 'Chelsea',
      away: 'Fulham'
    },
    winner: 'Fulham'
  }, ,
  {
    teams: {
      home: 'Arsenal',
      away: 'Tottenham'
    },
    winner: 'Arsenal'
  }
];

const newState = immer.default(initialFixtures, draft => {
  draft[1].winner = "something";
});

console.log(newState);
<script src="https://cdn.jsdelivr/npm/[email protected]/dist/immer.umd.js"></script>

If you are fortable to use a class based approach, you can try something like this:

  • Create a class that holds property value for team.
  • Create a boolean property in this class, say isHomeWinner. This property will decide the winner.
  • Then create a getter property winner which will lookup this.isHomeWinner and will give necessary value.
  • This will enable you to have a clean toggle function: this.isHomeWinner = !this.isHomeWinner.

You can also write your toggleWinner as:

const toggleWinner = (index) => {
  const newArr = initialFixtures.slice();
  newArr[index].toggle();
  return newArr;
};

This looks clean and declarative. Note, if immutability is necessary then only this is required. If you are fortable with mutating values, just pass fixture.toggle to your react ponent. You may need to bind context, but that should work as well.

So it would look something like:

function Fixtures = ({ fixtures, toggleWinner }) => { 
  fixtures.map((fixture, index) => ( 
    <div>
      <p>{fixture.winner} </p>
      <button onClick = {() => fixture.toggle() }> Change Winner</button> 

      // or
      // <button onClick = { fixture.toggle.bind(fixture) }> Change Winner</button> 
    </div>
  ))
}

Following is a sample of class and its use:

class Fixtures {
  constructor(home, away, isHomeWinner) {
    this.team = {
      home,
      away
    };
    this.isHomeWinner = isHomeWinner === undefined ? true : isHomeWinner;
  }

  get winner() {
    return this.isHomeWinner ? this.team.home : this.team.away;
  }

  toggle() {
    this.isHomeWinner = !this.isHomeWinner
  }
}

let initialFixtures = [
  new Fixtures('Liverpool', 'Manchester Utd'),
  new Fixtures('Chelsea', 'Fulham', false),
  new Fixtures('Arsenal', 'Tottenham'),
];

const toggleWinner = (index) => {
  const newArr = initialFixtures.slice();
  newArr[index].toggle();
  return newArr;
};

initialFixtures.forEach((fixture) => console.log(fixture.winner))
console.log('----------------')
initialFixtures = toggleWinner(1);
initialFixtures.forEach((fixture) => console.log(fixture.winner))

initialFixtures = toggleWinner(2);
console.log('----------------')
initialFixtures.forEach((fixture) => console.log(fixture.winner))

      const toggleWinner = (index) => {
        let  updatedFixtures = [...fixtures].splice(index, 1, {...fixtures[index],
         winner: fixtures[index].winner === fixtures[index].teams.home
         ? fixtures[index].teams.away : fixtures[index].teams.home})

        setUpdateFixtures(updatedFixtures);
      }
发布评论

评论列表(0)

  1. 暂无评论