Компонент React не будет отображать возврат функции

#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_componentwillmount

3. Приятно знать и сделаю. Я слышал об устаревании «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()’

https://jsfiddle.net/leolima/L7c418kp/13/

Комментарии:

1. Спасибо за ответ Leo. Похоже, что это тоже сработало бы, но я уже получил исчерпывающий ответ от Bird. Тем не менее, спасибо за помощь.