#javascript #arrays #reactjs #state #render
#javascript #массивы #reactjs #состояние #визуализация
Вопрос:
У меня есть компонент, который должен отображать «карточки», содержащие информацию, сохраненную в массиве в состоянии. Я получаю эту информацию из файла JSON и сохраняю ее в свой массив в «componentDidMount». Затем я создал функцию с именем «displayFood» перед возвратом моей функции рендеринга, которую я затем использую при возврате моих функций рендеринга. Проблема в том, что при запуске кода карточки не отображаются.
import React from 'react'
import "./style.scss";
import FoodData from "./foodData.json";
class Food extends React.Component {
constructor(props){
super(props);
this.state={
foodData: []
}
}
componentDidMount(){
FoodData.map((foodItem,index) => {
var foodTemp = {
id: index,
name: foodItem.name,
pic: foodItem.pic,
serving: foodItem.serving,
calories: foodItem.calories,
sugar: foodItem.sugar,
protien: foodItem.protien
}
this.setState((prevState) => {
prevState.foodData.push(foodTemp)
})
})
}
render() {
const displayFood = () => {
var i;
for(i = 0; i < this.state.foodData.length; i ){
return(
<div className="food-card">
<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 name={this.state.foodData[i].name}>Add</button>
</div>
)
}
}
return(
<div>
<div className="food-container">
{
displayFood()
}
</div>
</div>
)
}
}
export default Food;
Ответ №1:
Во-первых, я не уверен, что вы setState
в componentDidMount
работе. Если вы не хотите возвращать новый массив, вам не следует использовать map
, используйте forEach
вместо этого. И вы напрямую изменяете состояние, что не рекомендуется. Функции должно понравиться это:
componentDidMount(){
// Build a new array of object 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 })
}
Во-вторых, цикл return
in for
вернет только первый элемент. Вы должны создать массив, а затем пропустить элемент через цикл вместо возврата
const displayFood = () => {
let cards = [] // Crate an array
for(let i = 0; i < this.state.foodData.length; i ){
cards.push( // push item to array through the loop
<div className="food-card">
<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 name={this.state.foodData[i].name}>Add</button>
</div>
)
}
return cards // Return the array
}
И я рекомендую вам переместить displayFood
вне render()
функции. Потому что, когда другие вещи вызывают повторный рендеринг, но не this.state.foodData
изменение, компоненту приходится перестраивать displayFood()
. Это впустую.
Комментарии:
1. Черт возьми, ты ударил его по голове. Я реализовал ваши предложения вместе с изменением «DidMount» на «WillMount», и это отлично работает. Я хотел бы, чтобы у меня было больше репутации stackoverflow, чтобы дать вам видимый результат. Еще раз спасибо
2. Добро пожаловать! На самом деле, вы должны поместить его в
componentDidMount
, просто добавьте дескриптор пустых FooData в функцию render() . т. е.if(!this.state.dataFood.length) return <h1>Loading...</h1>
. Поскольку в реальном приложении данные будут поступать не из файла JSON, а из API,componentWillMount
это не может вам помочь в тот раз. И начиная с версии 17, ‘componentWillMount’ будет устаревшим reactjs.org/docs/react-component.html#unsafe_componentwillmount3. Приятно знать и сделаю. Я слышал об устаревании «compnentWillMount», но еще не изучал это. Вы, кажется, очень хорошо разбираетесь в React.
Ответ №2:
Согласно жизненному циклу, render
метод фактически уже вызывался ранее componentDidMount
. Следовательно, к моменту его отображения данные все еще пусты. Почему бы не перенести инициализацию данных в componentWillMount
?
Комментарии:
1. Благодаря justelouise я пошел дальше и изменил «DidMount» на «WillMount», и теперь он правильно отображает первый объект в массиве. Надеюсь, я смогу устранить причину, по которой цикл for дает мне только один результат. (я пытался проголосовать за вас, но поскольку у меня нет репутации на этом сайте, это не будет отображаться)
Ответ №3:
попробуйте изменить предыдущее состояние… вы использовали непосредственно данные о состоянии, это не изменяет корень состояния:
this.setState((prevState) => ({
...prevState,
foodData: [ ...foodData, foodTemp ],
}))
Комментарии:
1. Спасибо, я внес изменения и получаю тот же результат, что и программа, отображающая только первый элемент в массиве состояния
Ответ №4:
class Hello extends React.Component {
constructor(props){
super(props);
this.state={
foodData: []
}
}
componentDidMount(){
const { foodData } = this.state;
foodData.push({
id: 0,
name: 'beans',
pic: 'picurl',
serving: '2',
calories: '100',
sugar: '30',
protien: '10'
});
this.setState({ foodData });
}
render() {
return <div>
<div className="food-container">
{
this.state.foodData.map((item,key) => {
return <div className="food-card">
{`${item.pic}`}
<ul>
<li>{item.name}</li>
<li>Serving: {item.serving}</li>
<li>Calories: {item.calories}</li>
<li>Sugar: {item.sugar}</li>
<li>Protien: {item.protien}</li>
</ul>
<button name={item.name}>Add</button>
</div>
})
}
</div>
</div>;
}
}
ReactDOM.render(
<Hello />,
document.getElementById('container')
);
убедитесь, что ваши foodData правильно загружены в ‘componentDidMount()’
Комментарии:
1. Спасибо за ответ Leo. Похоже, что это тоже сработало бы, но я уже получил исчерпывающий ответ от Bird. Тем не менее, спасибо за помощь.