Реагировать - проблема с синтаксисом jsx и как выполнять итерацию с картой и отображать элементы на новых строках

Я новичок в React и делаю список дел в стиле Приложение "Список рецептов". У меня есть функциональный компонент Item.js, и я использую JSX и функцию карты для перебора каждого элемента рецепта и их отображения. Я хочу, чтобы каждый элемент рецепта отображался в новой строке, но когда я использую .map для итерации по ним, React помещает весь список элементов рецепта в один тег p вместо одного тега p для каждого элемента.

Как я могу перебирать элементы рецепта и отображать их в отдельных строках? Даже если я попытаюсь отобразить их как неупорядоченный список, React захочет поместить каждый элемент в один тег li.

Вот мой код:

import React from 'react';
import Button from 'react-bootstrap/lib/Button';


const Item = (props) => (
  <div>
    <div className="Recipe-Item-Container" key={props.text}>

        {props.items.map((item, index) => 
        <div className="Recipe-Item" key={index}>

            <h3>{item}</h3>

            <p>{props.ingredients[index]}</p>

          <div className="buttons-container">
            <Button className="edit-button" onClick={() => props.edit(item, index)}>Edit</Button>
            <Button className="delete-button" onClick={() => props.delete(item, index)}>Delete</Button>
          </div>

        </div>
      )}
    </div>
  </div>
)


export default Item;

Также в строке {props.items.map((item, index) => , если я добавлю фигурную скобку после =>, я получу сообщение об ошибке. У меня установлен линтер React / JSX, и он ничего не ловит. В чем проблема?

Я знаю, что это, вероятно, ошибка новичка, но JSX бросает меня в зацикливание.

.map требует return
Mark C. 13.09.2018 21:25
0
1
263
2

Ответы 2

Если вы добавите фигурные скобки после жирной стрелки, вам придется явно вернуть JSX.

const Item = (props) => (
  <div>
    <div className="Recipe-Item-Container" key={props.text}>

      {props.items.map((item, index) => {
          return (
              <div className="Recipe-Item" key={index}>

              <h3>{item}</h3>

              <p className="ingredients-list">
                   {props.ingredients[index].map((ingredient, ingredientIndex) => {
                      return (
                           <div className="ingredient" key={ingredient}>
                               {ingredient}
                           </div>
                     )
                   }}
              </p>

              <div className="buttons-container">
                <Button className="edit-button" onClick={() => props.edit(item, index)}>Edit</Button>
                <Button className="delete-button" onClick={() => props.delete(item, index)}>Delete</Button>
              </div>

            </div>
          )
        }
      )}
    </div>
  </div>
)

Это решило проблему фигурных скобок, спасибо. Но как я могу отобразить каждый элемент в новой строке? Нужно ли мне использовать второй .map в теге p?

Nick Kinlen 13.09.2018 21:40

Если props.ingredients[index] выдает несколько значений, тогда вам придется снова отобразить их и заключить в контейнер.

Sushanth -- 13.09.2018 21:43

Это решает проблему отображения каждого элемента в новой строке, но, к сожалению, это нарушает мою программу, и теперь я получаю ошибку TypeError: props.ingredients[index].map is not a function, когда пытаюсь редактировать список ингредиентов при нажатии кнопки.

Nick Kinlen 13.09.2018 22:12

Что на выходе props.ingredients[index]. Поскольку карты работают только с объектами массива. Также как выглядят функции delete и edit?

Sushanth -- 13.09.2018 22:17

Начальное состояние - это массив, но у меня есть функция handleChange и функция onEditSubmit, которые принимают отредактированные пользователем элементы и обновляют состояние. Может ли это быть проблемой? Вот код. github.com/Nicknyr/FCC_Recipe_Box/blob/master/src/App.js#L25‌]

Nick Kinlen 13.09.2018 23:09

Вот рабочая версия.

class App extends React.Component {
  state = {
    items: [ "Pumpkin Pie", "Spaghetti", "Onion Pie" ],
    ingredients: [
      [ "Pumpkin Puree", "Sweetened Condensed Milk", "Eggs", "Pumpkin Pie Spice", "Pie Crust" ],
      [ "Noodles", "Tomatoe", "Sauce", "Meatballs" ],
      [ "Onion", "Pie Crust" ],
    ],
  }

  render() {
    return (
      <div className="box">
        <Item items={this.state.items} ingredients={this.state.ingredients} />
      </div>
    );
  }
}

const Item = props => (
  <div>
    <div className="Recipe-Item-Container" key={props.text}>

      {props.items.map( ( item, index ) => (
        <div className="Recipe-Item" key={item}>

          <h3>{item}</h3>
          <ul>
            {
              props.ingredients[ index ].map( ingredient =>
                <li key={ingredient}>{ingredient}</li> )
            }
          </ul>


          <div className="buttons-container">
            <button className="edit-button" onClick={() => props.edit( item, index )}>Edit</button>
            <button className="delete-button" onClick={() => props.delete( item, index )}>Delete</button>
          </div>

        </div>
      ) )}
    </div>
  </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>

Но на вашем месте я бы изменил свою форму состояния. Что-то подобное:

class App extends React.Component {
  state = {
    items: [
      {
        name: "Pumpkin Pie",
        ingredients: [
          "Pumpkin Puree",
          "Sweetened Condensed Milk",
          "Eggs",
          "Pumpkin Pie Spice",
          "Pie Crust"
        ]
      },
      {
        name: "Spaghetti",
        ingredients: ["Noodles", "Tomatoe", "Sauce", "Meatballs"]
      },
      {
        name: "Onion Pie",
        ingredients: ["Onion", "Pie Crust"]
      }
    ]
  };

  removeItem = item => {
    const newItems = this.state.items.filter(el => el.name !== item.name);
    this.setState({ items: newItems });
  };

  editItem = item => alert(`${item.name} will be edited`);

  renderItems = () =>
    this.state.items.map(item => (
      <Item
        key={item.name}
        item={item}
        removeItem={this.removeItem}
        editItem={this.editItem}
      />
    ));

  render() {
    return <div className="box">{this.renderItems()}</div>;
  }
}

const Item = props => {
  const { item, removeItem, editItem } = props;
  const handleRemove = () => removeItem(item);
  const handleEdit = () => editItem(item);

  return (
    <div>
      <div className="Recipe-Item-Container" key={props.text}>
        <div className="Recipe-Item">
          <h3>{item.name}</h3>
          <ul>
            {item.ingredients.map(ingredient => (
              <li key={ingredient}>{ingredient}</li>
            ))}
          </ul>
          <div className="buttons-container">
            <button className="edit-button" onClick={handleEdit}>
              Edit
            </button>
            <button className="delete-button" onClick={handleRemove}>
              Delete
            </button>
          </div>
        </div>
      </div>
    </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>

Изменения

  • Форма состояния: вместо двух массивов мы сохраняем объект для каждого элемента. У этого объекта есть свойства name и ingredients. Может, в будущем у него появится уникальный id? Объекты гибкие.
  • Вместо того, чтобы передавать все элементы в компонент Item, мы сопоставляем элементы в родительском компоненте и передаем только один элемент в компонент Item.
  • У нас все еще есть функции-обработчики, определенные в родительском элементе. Но мы не используем их непосредственно в обратном вызове кнопки с функцией стрелки. Таким образом, они не воссоздаются в каждом рендере. Кроме того, нам не нужно использовать индекс для передачи элементов обратно родительскому элементу. У нас есть сама опора item! Вы можете увидеть, как мы справляемся с функцией удаления: с .filter Вы можете применить ту же функцию к другим функциям. .map, .filter, Object.assign или синтаксис распространения - все это хорошие инструменты. Просто избегайте прямого изменения вашего состояния.

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