React - "Is not a function" 将函数作为 props 传递给功能组件时出错

React - "Is not a function" error when passing function as props to functional component

我是 React 的新手,我正在研究 ToDo 列表样式 Recipe app. I asked a previous question here 关于重构功能组件,以便配方成分出现在单独的行上,而不是全部显示为一个单行段落.

我实施了建议的更改并使用了第二个 .map 函数来遍历成分并成功地让它们出现在不同的行中。但是,现在实施更改后,当用户单击编辑按钮并尝试编辑配方成分时,我会收到错误消息。

处理显示食谱名称和成分的函数组件 (Item.js) 如下所示:

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) => {
        return (
        <div className="Recipe-Item" key={index}>
          <h3>{item}</h3>

        // This p and the map function within it were the changes I made
        <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>
)

export default Item;

现在,当我单击编辑按钮并尝试编辑配料表时,出现此错误:

这是我的 App.js 组件,它存储状态并将数据传递给 Item.js:

import React, { Component } from 'react';
import Item from './Item';
import './App.css';
import ModalComponent from './Modal.js';
import Button from 'react-bootstrap/lib/Button';
import EditModalComponent from './EditModal.js';

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      items: ["Pumpkin Pie", "Spaghetti", "Onion Pie"],
      ingredients:[
        ["Pumpkin Puree ", "Sweetened Condensed Milk ", "Eggs ", "Pumpkin Pie Spice ", "Pie Crust "],
        ["Noodles ", "Tomato Sauce ", "(Optional) Meatballs "],
        ["Onion ", "Pie Crust "]
      ],

      // Recipe name and ingredients
      inputVal: '',
      ingredientVal: '',
      // Recipe name and ingredients when user is editing existing recipe
      inputValEdit: '',
      ingredientValEdit: '',
      // Controls whether forms are displayed or hidden
      showRecipeForm: false,
      showRecipeEditForm: false,
      // Index to select which recipe item is being edited
      editingIndex: ''
    };

  }

  // Get text user inputs for recipes
  handleChange = (event) => {
    this.setState({ [event.target.name]: event.target.value });
  };


  // When user submits recipe this adds it to the list
  onSubmit = (event) => {
    event.preventDefault()
    this.setState({
      items: [...this.state.items, this.state.inputVal],
      ingredients: [...this.state.ingredients, this.state.ingredientVal],
      showRecipeForm: false
    });
  }

  onEditSubmit = (event) => {
    event.preventDefault();
    const {items, ingredients, inputValEdit, ingredientValEdit, editingIndex} = this.state;

    // Selects proper recipe item to edit
    items[editingIndex] = inputValEdit;
    ingredients[editingIndex] = ingredientValEdit;

    this.setState({
      items: items,
      ingredients: ingredients,
      inputVal: '',
      ingredientVal: '',
      showRecipeEditForm: false
    });
  }

  closeRecipeForm = () => {
    this.setState({
      showRecipeForm: false,
      showRecipeEditForm: false
    });
  }

  // Shows recipe
  AddRecipe = (bool) => {
    this.setState({
      showRecipeForm: bool
    });
  }

  // Is called when one of the edit recipe buttons is clicked, shows RecipeEditForm
  edit = (item, index) => {
    this.setState({
      showRecipeEditForm: !this.state.showRecipeEditForm,
      editingIndex: index
    });
  }

  // Deletes recipe item from the list
  delete = (item, index) => {
     this.setState({
      ingredients : this.state.ingredients.filter((_, i) => i !== index),
      items: this.state.items.filter((_, i) => i !== index)
    });
  }


  render() {
    return (
      <div className="container">
        <h1>Recipe List</h1>


        <ModalComponent
          inputVal={this.state.inputVal}
          handleChange={this.handleChange}
          ingredientVal={this.state.ingredientVal}
          onSubmit={this.onSubmit}
          addRecipe={this.addRecipe}
          showRecipeForm={this.state.showRecipeForm}
          closeRecipeForm={this.closeRecipeForm}
        />

        <EditModalComponent
          inputValEdit={this.state.inputValEdit}
          handleChange={this.handleChange}
          ingredientValEdit={this.state.ingredientValEdit}
          onEditSubmit={this.onEditSubmit}
          closeRecipeForm={this.closeRecipeForm}
          addRecipe={this.addRecipe}
          showRecipeEditForm={this.state.showRecipeEditForm}
        />


        <Item
          items={this.state.items}
          ingredients={this.state.ingredients}
          edit={this.edit}
          delete={this.delete}
        />

      <Button className="add-recipe-button" onClick={this.AddRecipe}>Add New Recipe</Button>

      </div>
    );
  }

}

当用户尝试编辑成分列表时,为什么我会在 Item.js 中收到此错误?我认为它涉及 onEditSubmit 功能,但我不确定。

所以,我在你的 onEditSubmit 中看到你正在这样做:

ingredients[editingIndex] = ingredientValEdit; // line 57

你的状态中的成分实际上是一个数组的数组(多维)。 是这样的:

ingredients:[
        ["Pumpkin Puree ", "Sweetened Condensed Milk ", "Eggs ", "Pumpkin Pie Spice ", "Pie Crust "],
        ["Noodles ", "Tomato Sauce ", "(Optional) Meatballs "],
        ["Onion ", "Pie Crust "]
      ],

但是在那行代码之后你的数组变成了这样: 我已经输入 'Chicken Lollipop' 作为例如

的成分
ingredients:[
        "Chicken Lollipop",
        ["Noodles ", "Tomato Sauce ", "(Optional) Meatballs "],
        ["Onion ", "Pie Crust "]
      ],

因此,所选索引处的值变为字符串而不是数组。 在这种情况下,索引 0 处的值现在是一个字符串。

因此,Item.js 中的渲染函数中断了,因为它试图映射它不能映射的字符串。

要解决此问题,您可以将第 57 行更改为:

ingredients[editingIndex] = [ingredientValEdit];

现在,每个条目都将正确存储为一个数组,您的代码应该可以工作了。 更新后的数组如下所示:

ingredients:[
        ["Chicken Lollipop"],
        ["Noodles ", "Tomato Sauce ", "(Optional) Meatballs "],
        ["Onion ", "Pie Crust "]
      ]

附加内容(此答案不需要):

但是,由于您始终需要将成分作为数组,因此您还可以使用分隔符拆分输入值,例如:

用户输入的成分为:'Lollipop, Candy, Something'

在你的第 57 行你也可以这样做:

ingredients[editingIndex] = ingredientValEdit.split(',');