React - JSX 语法问题,以及如何使用地图进行迭代并在换行符上显示项目

我是 React 菜鸟,正在制作一个 ToDo 列表样式Recipe List app.我有一个功能组件 Item.js,我正在使用 JSX 和 map 函数来遍历每个配方项并显示它们.我希望每个食谱项都出现在新行上,但是当我使用 .map 遍历它们时,React 会将整个食谱项列表放入一个 p 标签,而不是每个项目的一个 p 标签.

如何遍历配方项并将它们显示在不同的行上?即使我尝试将它们显示为无序列表,React 也希望将每个项目放入一个 li 标签中.

这是我的代码:

从'react'导入反应;从'react-bootstrap/lib/Button'导入按钮;const Item = (props) =>(

<div className="配方-项目-容器";键={props.text}>{props.items.map((item, index) =><div className=配方-项目";键={索引}><h3>{项目}</h3><p>{props.ingredients[index]}</p><div className="buttons-container"><Button className="编辑按钮";onClick={() =>props.edit(item, index)}>编辑</按钮>

同样在 {props.items.map((item, index) => 行,如果我在 => 之后添加一个花括号,我得到一个错误.我有一个 React/安装了 JSX linter,但它没有捕获任何东西.这是什么问题?

我知道这可能是一个菜鸟错误,但 JSX 在这里让我陷入了循环.

解决方案

这是工作版本.

class App extends React.Component {状态 = {项目:[南瓜派"、意大利面"、洋葱派"]、配料: [[南瓜泥"、甜炼乳"、鸡蛋"、南瓜派香料"、派皮"]、[面条"、番茄"、酱汁"、肉丸"]、[洋葱",馅饼皮"],],}使成为() {返回 (<div className="box"><Item items={this.state.items} 成分={this.state.ingredients}/></div>);}}常量项目 = 道具 =>(

<div className="Recipe-Item-Container" key={props.text}>{props.items.map( ( 项目, 索引 ) => (<div className="Recipe-Item" key={item}><h3>{项目}</h3><ul>{props.ingredients[索引].map(成分=><li key={ingredient}>{ingredient}</li>)}</ul><div className="按钮容器"><button className="edit-button" onClick={() =>props.edit(item, index)}>编辑</按钮><button className="delete-button" onClick={() =>props.delete(item, index)}>删除</按钮></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 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 {状态 = {项目: [{名称:南瓜派",配料: [南瓜泥",甜炼乳",蛋",南瓜派香料",馅饼皮"]},{名称:意大利面",食材:[面条"、番茄"、酱汁"、肉丸"]},{名称:洋葱派",原料:[洋葱"、派皮"]}]};removeItem = 项目 =>{const newItems = this.state.items.filter(el => el.name !== item.name);this.setState({ items: newItems });};编辑项目 = 项目 =>alert(`${item.name} 将被编辑`);渲染项 = () =>this.state.items.map(item => (<项目键={item.name}项目={项目}removeItem={this.removeItem}editItem={this.editItem}/>));使成为() {return <div className="box">{this.renderItems()}</div>;}}常量项目 = 道具 =>{const { item, removeItem, editItem } = props;常量句柄移除 = () =>删除项目(项目);常量句柄编辑 = () =>编辑项目(项目);返回 (

<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="按钮容器"><button className="edit-button" onClick={handleEdit}>编辑</按钮><button className="delete-button" onClick={handleRemove}>删除</按钮></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 src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script><div id="root"></div>

变化

  • 状态形状:我们不是保存两个数组,而是为每个项目保存一个对象.该对象具有 nameingredients 属性.也许在未来,它可能有一个唯一的id?对象很灵活.
  • 我们不是将所有项目都传递给 Item 组件,而是将项目映射到父组件中,然后仅将一项传递给 Item 组件.
  • 我们仍然在父级中定义了处理函数.但是,我们并没有直接在按钮的回调函数中使用它们和箭头函数.因此,它们不会在每次渲染中重新创建.此外,我们不必使用索引将项目传递回父项.我们有 item 道具本身!您可以看到我们如何处理删除功能:使用 .filter 您可以将相同的功能应用于其他功能..map.filterObject.assign 或扩展语法都是很好的工具.只是,避免直接改变你的状态.

I'm a React noob and making a ToDo list style Recipe List app. I have a functional component, Item.js, and I am using JSX and the map function to iterate through each recipe item and display them. I want each recipe item to appear on a new line, but when I use .map to iterate through them React puts the entire list of recipe items into one p tag instead of one p tag for each item.

How can I iterate through the recipe items and display them on separate lines? Even if I try to display them as an unordered list React wants to throw each item in one li tag.

Here is my code:

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;

Also on the {props.items.map((item, index) => line if I add a curly brace after the => I get an error. I have a React/JSX linter installed and it isn't catching anything. What's the issue?

I know this is probably a noob error but JSX is throwing me for a loop here.

解决方案

Here is the working version.

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>

But if I were you I would change my state shape. Something like that:

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>

Changes

  • State shape: Instead of holding two arrays, we are keeping an object per item. This object has a name and ingredients property. Maybe in the future, it may have a unique id? Objects are flexible.
  • Instead of passing all the items to an Item component, we are mapping the items in the parent component and pass just one item to the Item component.
  • We still have handler functions defined in the parent. But, we are not using them directly in the button's callback with an arrow function. So, they are not recreated in every render. Also, we don't have to use an index to pass the items back to the parent. We have the item prop itself! You can see how we handle the remove functionality: with .filter You can apply the same functionality to other functions. .map, .filter, Object.assign or spread syntax are all good tools. Just, avoid mutating your state directly.

相关文章