Как удалить и добавить элементы массива в объект, используемый для состояния?

Я пытаюсь создать эту функцию, которую можно использовать для добавления this.state.user.id в this.state.players.team1, а также проверять все другие массивы в this.state.players, чтобы удалить this.state.user.id, если он существует.

Вот некоторый контекст:

// function is used for onClick()
// this.state.players = { 
//     team1: [1, 2], 
//     team2: [5, 7] 
// }
// this.state.user.id = 7
// (team) parameter is passed with "team1" or "team2" depending on situation

Ниже представлена ​​функция, которую я пытался написать. Есть ли лучший способ записать это? this.setState работает асинхронно, что нарушает мою функцию.

onJoinTeam(team) {

    // remove player id from any id
    this.setState({
        players: 
            Object.entries(this.state.players).map(key => {
                return key[1].filter(player => player !== this.state.user.id)
            })
    })

    // add this.state.user.id to this.state.players
    this.setState({  
        players: { 
            ...this.state.players, 
            [team]: [ ...this.state.players[team], this.state.user.id ], 
        }
    })
}

Сначала создайте полностью новый объект players. Затем используйте один вызов setState. Другой, но худший способ - использовать тот факт, что setState принимает обратный вызов, который выполняется после изменения состояния, и меняет состояние во второй раз.

Chris G 23.08.2018 14:20
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
75
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я бы попробовал что-то вроде

const { players } = this.state;

for (const team in players) {
  const teamPlayers = players[team];
  const playerIndex = teamPlayers.findIndex(tp => tp === userId);

  if (playerIndex !== -1) {
    teamPlayers.splice(playerIndex, 1);
    break;
  }
}

players[newTeam].push(userId);

this.setState({ players });

Можно увидеть в действии в этом фрагменте

class App extends React.Component {
  state = {
    players: {
      team1: [1, 2, 3],
      team2: [4, 5, 6],
    },
  };

  render() {
    return (
      <div>
        <pre>{JSON.stringify(this.state, null, 4)}</pre>

        <form onSubmit = {(e) => this.onJoinTeam(e)}>
          <label>User ID</label>
          <input name = "userId" type = "number" min = "1" max = "6" step = "1" />
          <br />
          <label>New Team</label>
          <select name = "newTeam">
            <option value = "team1">Team 1</option>
            <option value = "team2">Team 2</option>
          </select>
          <br />
          <button type = "submit">Move player</button>
        </form>
      </div>
    );
  }

  onJoinTeam(e) {
    e.preventDefault();

    const newTeam = e.currentTarget.newTeam.value;
    const userId = parseInt(e.currentTarget.userId.value, 10);

    const { players } = this.state;

    for (const team in players) {
      const teamPlayers = players[team];
      const playerIndex = teamPlayers.findIndex(tp => tp === userId);

      if (playerIndex !== -1) {
        teamPlayers.splice(playerIndex, 1);
        break;
      }
    }

    players[newTeam].push(userId);

    this.setState({ players });
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id = "root"></div>

Это отличное решение, но я не могу использовать его в своей настройке. У меня есть функция сопоставления, которая отображает контент вместе с кнопками в зависимости от количества команд. Ваш код дублирует мое состояние всякий раз, когда я нажимаю другую кнопку Присоединиться к команде. Есть еще предложения?

kayq 24.08.2018 01:01

@kayq Я не знаю, что означает «дублирование моего состояния», но я не вижу причины, по которой это не работает с несколькими кнопками.

ed' 24.08.2018 09:39

@EdwardSammutAlessi, здесь вы изменяете исходное состояние. Удаление players из состояния с последующим изменением его свойства также изменяет здесь исходный players. Даже если вы используете что-то вроде синтаксиса распространения, например const state = { ...this.state.player }, это не сработает, поскольку синтаксис распространения создает только неглубокую копию, снова изменение свойства изменяет исходный объект. Также splice изменяет исходный массив, это вторая мутация. Самый простой способ - сделать глубокий клон чего-то вроде этого: const players = JSON.parse(JSON.stringify(this.state.players));

devserkan 24.08.2018 14:35

Кроме того, если вы используете break в условном выражении if (я не понимаю, почему), нарушает код для объекта проигрывателей, у которого есть элементы больше 2.

devserkan 24.08.2018 14:36

Вы правы, что я изменяю исходное состояние, но для моего примера это не имеет особого значения. Сделал выбор для краткости.

ed' 24.08.2018 20:32
Ответ принят как подходящий

Вот еще одно решение, не такое простое, возможно, не очень читаемое, но оно работает :) Используя Object.entries, создаем карту. При этом мы проверяем, соответствует ли team нашему key. Если да, мы добавляем наш id к нашему key, если нет, мы отфильтровываем id. Последний шаг - создать объект, используя этот массив с reduce.

class App extends React.Component {
  state = {
    players: {
      team1: [1, 2],
      team2: [5, 7],
      team3: [9, 6, 7],
    },
    id: 7,
  }

  componentDidMount() {
    this.change( "team1" );
  }

  change = team => {
    const { players, id } = this.state;
    const newPlayers = Object.entries(players).map(([key,value]) =>
      team === key
        ? { [key] : [ ...value, id ] }
        : { [key]: value.filter(player => player !== id) }
    ).reduce( ( obj, item) => ({
      ...obj,
      ...item,
    }) );
    
    this.setState({players: newPlayers})
  }

  render() {
    console.info( this.state.players );
    return (
         <div>
         Look the console to see the old and new value of the players.
         </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id = "root"></div>

Спасибо, это то, что я искал. Это работает для моей установки, но я согласен с вами, что смотреть на это некрасиво. Я собираюсь попытаться описать это дальше, но я буду благодарен за любую дополнительную помощь. Еще раз спасибо!

kayq 24.08.2018 01:15

Я нашел решение после прочтения всех ваших идей. Вот что у меня есть сейчас:

onJoinTeam(team) {
    const { players } = this.state;
    const newTeams = {}
    Object.entries(players).map(([key, value]) => {
        const filteredTeamObj = { 
            [key]: value.filter(player => player !== this.state.user.id) }
        return Object.assign(newTeams, filteredTeamObj)
    })
    this.setState({
        players: { 
            ...newTeams, 
            [team]: [ ...this.state.players[team], this.state.user.id ], 
        }
    })
}

Еще раз спасибо всем

Хорошее сочетание там :)

devserkan 24.08.2018 14:09

Другие вопросы по теме