#arrays #reactjs #state #updating
#массивы #reactjs #состояние #обновление
Вопрос:
Что я пытаюсь сделать, так это обновить массив в состоянии, добавив строку с названием кнопки, которую нажал пользователь. У пользователя есть несколько кнопок, поэтому после нажатия на них всех у меня должен остаться массив строк, соответствующих нажатиям кнопок пользователем.
Сначала я даю пользователю оповещение о том, какой продукт был добавлен, затем я просто пытаюсь добавить название этого продукта в массив в состоянии. После чего я передаю этот массив обратно родительскому компоненту, чтобы обновить аналогичный массив там.
Проблема, с которой я сталкиваюсь, заключается в том, что когда я нажимаю на один из параметров питания, массив в состоянии не обновляется, но я получаю правильное предупреждение. Когда я пытаюсь добавить второе питание, массив в состоянии обновляется с первым выбранным мной вариантом. С тех пор каждый раз, когда я пытаюсь добавить элемент питания, массив в состоянии обновляется тем продуктом, который я выбрал перед этим.
Я полагаю, что проблема в том, что я обновляю состояние таким образом, который React не выполняет в ближайшее время. Я знаю, что обновление состояния в React не гарантируется.
Извините за длинный пост, спасибо за ваше время.
import React from "react";
import "./style.scss";
import FoodData from "./foodData.json";
class Food extends React.Component {
constructor(props) {
super(props);
this.state = {
foodData: [],
addedFoods: []
};
this.handleClick = this.handleClick.bind(this);
}
componentDidMount() {
// Build a new array of objects from FoodData
const newFoodData = FoodData.map(
({ name, pic, serving, calories, sugar, protien }, index) => ({
id: index,
name,
pic,
serving,
calories,
sugar,
protien
})
);
// Assign the new object to state
this.setState({ foodData: newFoodData });
}
handleClick(e) {
alert("Added " e.target.name);
this.setState(
{
addedFoods: [...this.state.addedFoods, e.target.name]
},
this.props.updateAddedFoods(this.state.addedFoods)
);
}
render() {
const displayFood = () => {
let foodItems = []; // Crate an array
for (let i = 0; i < this.state.foodData.length; i ) {
foodItems.push(
// push item to array through the loop
<div className="food-card" key={i}>
<img src={require(`${this.state.foodData[i].pic}`)} />
<ul>
<li>{this.state.foodData[i].name}</li>
<li>Serving: {this.state.foodData[i].serving}</li>
<li>Calories: {this.state.foodData[i].calories}</li>
<li>Sugar: {this.state.foodData[i].sugar}</li>
<li>Protien: {this.state.foodData[i].protien}</li>
</ul>
<button
onClick={this.handleClick}
name={this.state.foodData[i].name}
>
Add
</button>
</div>
);
}
return foodItems;
};
return (
<div>
<div className="food-container">
{!this.state.foodData.length ? <h1>Loading ...</h1> : displayFood()}
</div>
</div>
);
}
}
export default Food;
Комментарии:
1. Здравствуйте! Просто чтобы быть уверенным.. Я вижу, что в
handleClick(e)
вы выполняете обратный вызовthis.props.updateAddedFoods
. Не могли бы вы, пожалуйста, в этом обратном вызове добавитьconsole.log(this.state.addedFoods)
? Просто чтобы посмотреть, обновлено ли состояние, переданное этой функции, уже или нет, потому что проблема может быть в том, как вы обновляете состояние вhandleClick
2. Конечно, я попробую прямо сейчас… И я получаю точно то же самое. При первой попытке добавить элемент питания журнал консоли выводит пустой массив, в котором должно быть только что добавленное питание. Затем, когда я пытаюсь добавить еще один элемент питания, журнал консоли выводит массив с одним элементом, тем, который я пытался добавить первым.
3. Хм, хорошо, я добавляю ответ с примером, дайте мне знать, если это поможет
4. Спасибо, что помогли мне, но это работает. Я все же ценю это. Если бы я мог проголосовать за вас, я бы проголосовал.
Ответ №1:
Вторым аргументом для setState
должна быть функция, а не вызов функции. Также его лучше использовать setState(oldState => newState)
, когда новое состояние основано на старом:
handleClick(e) {
const name = e.target.name;
alert("Added " name);
this.setState(oldState => ({
addedFoods: [...oldState.addedFoods, name]
}),
() => this.props.updateAddedFoods(this.state.addedFoods)
);
}
Комментарии:
1. Спасибо за ввод и извините за длинный ответ, я пытаюсь сделать это сейчас…. Когда я вношу изменение, я получаю ошибку «TypeError: не удается прочитать свойство ‘name’, равное null». Пытаюсь устранить неполадки прямо сейчас, но вы наверняка правы насчет изменения состояния.
2. Попробуйте удалить
alert
или сохранитьe.target.name
перед этим. React повторно использует объекты событий и может быть очищен раньшеsetState
3. Все еще получаю ту же ошибку в «addedFood: […oldState.addedFoods, e.target.name ]» строка с предупреждением или без него.
4. Я обновил свой ответ. Объект события, хранящийся в
e
, очищается до того, как он попадет в обратный вызов внутриsetState
.5. Да, ты понял, дружище. Хотел бы я проголосовать за ваш ответ. Большое спасибо
Ответ №2:
setState
является асинхронным, поэтому, когда вы передаете this.state.addedFoods
в качестве аргумента в this.props.updatedAddedFoods
, вы передаете состояние до того, как вы его изменили. Поскольку кажется, что вы дублируете данные, могу ли я предложить, чтобы массив food был только у родительского элемента и передавал его дочернему элементу в качестве реквизита?
Комментарии:
1. Это действительно отличное изменение дизайна, которое я должен внести, вы абсолютно правы, и я сделаю это прямо сейчас.
Ответ №3:
Итак, как я сказал в комментарии, я думаю, handleClick()
проблема в том, как именно вы обновляете состояние.
Можете ли вы попробовать написать эту функцию следующим образом:
handleClick(e) {
const newAddedFoods = Object.assign([], this.state.addedFoods);
newAddedFoods.push(e.target.name);
this.setState({addedFoods: newAddedFoods }, () => this.props.updateAddedFoods(newAddedFoods));
}
Хотя, @Iarz абсолютно прав: кажется, у вас одинаковые данные как в родительском компоненте, так и в дочернем компоненте. Просто сохраните его в родительском компоненте и передайте дочерним элементам данные и функцию для его обновления.
Комментарии:
1. 100% прав насчет дубликатов данных в родительском и дочернем. Боли роста, лол. Спасибо за все советы.