Я пытаюсь создать эту функцию, которую можно использовать для добавления 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 ],
}
})
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я бы попробовал что-то вроде
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 Я не знаю, что означает «дублирование моего состояния», но я не вижу причины, по которой это не работает с несколькими кнопками.
@EdwardSammutAlessi, здесь вы изменяете исходное состояние. Удаление players из состояния с последующим изменением его свойства также изменяет здесь исходный players. Даже если вы используете что-то вроде синтаксиса распространения, например const state = { ...this.state.player }, это не сработает, поскольку синтаксис распространения создает только неглубокую копию, снова изменение свойства изменяет исходный объект. Также splice изменяет исходный массив, это вторая мутация. Самый простой способ - сделать глубокий клон чего-то вроде этого: const players = JSON.parse(JSON.stringify(this.state.players));
Кроме того, если вы используете break в условном выражении if (я не понимаю, почему), нарушает код для объекта проигрывателей, у которого есть элементы больше 2.
Вы правы, что я изменяю исходное состояние, но для моего примера это не имеет особого значения. Сделал выбор для краткости.
Вот еще одно решение, не такое простое, возможно, не очень читаемое, но оно работает :)
Используя 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>Спасибо, это то, что я искал. Это работает для моей установки, но я согласен с вами, что смотреть на это некрасиво. Я собираюсь попытаться описать это дальше, но я буду благодарен за любую дополнительную помощь. Еще раз спасибо!
Я нашел решение после прочтения всех ваших идей. Вот что у меня есть сейчас:
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 ],
}
})
}
Еще раз спасибо всем
Хорошее сочетание там :)
Сначала создайте полностью новый объект
players. Затем используйте один вызовsetState. Другой, но худший способ - использовать тот факт, чтоsetStateпринимает обратный вызов, который выполняется после изменения состояния, и меняет состояние во второй раз.