【问题标题】:How to get onClick in React to work on a single element with multiple siblings?如何让 React 中的 onClick 处理具有多个兄弟姐妹的单个元素?
【发布时间】:2020-11-22 09:09:02
【问题描述】:

我是 React 新手,在尝试触发 onClick 事件时遇到问题。我有事件工作,当它被点击时,div 出现并重新出现。问题是,如果我按下特定项目的按钮,所有的 div 都会出现,而不是我刚刚单击按钮的 div。如何使我单击的按钮仅在该特定元素上触发。

这是我的代码:

类 App 扩展 React.Component {

  constructor(props) {
    super(props)
    this.state = {
      userInput: '',
      getRecipe: [],
      ingredients: "none"
    }
  }

  handleChange = (e) => {
    this.setState({
      userInput: e.target.value
    })
  }
  

  handleSubmit = (e) => {
    e.preventDefault()

    const getData = () => {
      fetch(`https://api.edamam.com/search?q=${this.state.userInput}&app_id=${APP_ID}&app_key=${APP_KEY}&from=0&to=18`)
        .then(res => {
          return res.json()
        }).then(data => {
          this.setState({
            getRecipe: data.hits
          })
        })
    }
    getData()
  }
// this is where the button logic comes in
  getIngredients = (e) => {
    e.preventDefault()
    if (this.state.ingredients === 'none') {
      this.setState({
        ingredients: "block"
      })
    } else {
      this.setState({
        ingredients: "none"
      })
    }
  }


  render() {

    return (
      <div className="recipes">
        <Nav changed={this.handleChange} submit={this.handleSubmit} />
        <Content
          userInput={this.state.userInput}
          recipe={this.state.getRecipe}
          getIngredients={this.getIngredients}
          ingredients={this.state.ingredients} />
      </div>
    )
  }
}

const Content = ({ userInput, recipe, getIngredients, ingredients }) => {

    return (
        <div>
            <h2 className="userinputtitle"> {userInput} </h2>
            <div className="containrecipes">
                {recipe.map(rec => {
                    return (
                        <div key={rec.recipe.label} className="getrecipes">
                            <h1 className="recipetitle" key={rec.recipe.label}>{rec.recipe.label.toUpperCase()}</h1>
                            <img src={rec.recipe.image}></img>
                            <h4 className="health"> Health Labels: {rec.recipe.healthLabels.join(', ')}</h4>
                            <h4 > Diet Label: {rec.recipe.dietLabels}</h4>
                            <h4 > Calories: {Math.floor(rec.recipe.calories)}</h4>
                            <h4 className="cautions"> Cautions: {rec.recipe.cautions}</h4>
                            <div>
                                <h4>{rec.recipe.digest[0].label + ":" + " " + Math.floor(rec.recipe.digest[0].total) + "g"}</h4>
                                <h4>{rec.recipe.digest[1].label + ":" + " " + Math.floor(rec.recipe.digest[1].total) + "g"}</h4>
                                <h4>{rec.recipe.digest[2].label + ":" + " " + Math.floor(rec.recipe.digest[2].total) + "g"}</h4>
                            </div>
// the button is clicked here, yet all div fire at the same time
                            <button onClick={getIngredients} className="getingredients">Ingredients</button>
                            {rec.recipe.ingredients.map(i => {
                                return (
                                    <div style={{ display: ingredients }} className="containingredients">
                                        < ul className="ingredients">
                                            <li className="ingredient">{i.text}</li>
                                        </ul>
                                    </div>
                                )

                            })}
                        </div>

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

    )
}

【问题讨论】:

  • 要么为映射的数据添加一个属性,要么保留一个单独的数据结构来为要切换的特定元素保持“切换”状态。使用元素 ID 切换值。更新 getIngredients 回调以使用此唯一 ID。如果您遇到困难或实施问题,请尝试此操作并更新问题。
  • @DrewReese 我无法理解通过向映射数据添加属性的含义。
  • 我的意思是你可以扩充你的数据并为每个元素添加一个属性来切换。我在下面回答时只使用了一个额外的数据结构。

标签: javascript reactjs


【解决方案1】:

更新 getIngredients 以使用配方 ID 并将其保存在状态中。

切换单一配方成分

this.state = {
  userInput: '',
  getRecipe: [],
  ingredientsId: null
}

...

getIngredients = recipeId => e => {
  e.preventDefault();
  this.setState(prevState => ({
    ingredientsId: prevState.ingredientsId ? null : recipeId,
  }));
}

...

<Content
  userInput={this.state.userInput}
  recipe={this.state.getRecipe}
  getIngredients={this.getIngredients}
  ingredientsId={this.state.ingredientsId} // <-- pass id
/>

有条件地设置Content中的显示样式。

const Content = ({ userInput, recipe, getIngredients, ingredientsId }) => {

  ...

  <button
   onClick={getIngredients(recipe.id)} // <-- pass id
   className="getingredients"
  >
    Ingredients
  </button>
  <div
   style={{
     // set display style
     display: ingredientsId === recipe.id ? "block" : "none"
   }}
   className="containingredients"
  >
    <ul className="ingredients">
      <li className="ingredient">{i.text}</li>
    </ul>
  </div>

  ...

切换多个配方成分

同上,稍作改动

状态是地图

this.state = {
  userInput: '',
  getRecipe: [],
  ingredientsId: {},
}

在处理程序中切换 id

getIngredients = recipeId => e => {
  e.preventDefault();
  this.setState(prevState => ({
    ingredientsId: {
      ...prevState.ingredientsId,
      [recipeId]: !prevState.ingredientsId[recipeId]
    },
  }));
}

在传递的地图中查找recipeId

style={{
  // set display style
  display: ingredientsId[recipe.id] ? "block" : "none"
}}

【讨论】:

  • @Alanaj "state",或者在本例中命名为 prevState,是 API 的一部分 setState 更新程序函数的第一个参数。 reactjs.org/docs/react-component.html#setstate 通常称为功能状态更新。
  • @Alanaj 如果这有助于解决您的问题,那么请接受回答并投票,干杯。
  • @Alanaj 并不是真的一个人变成另一个人,而是有点相反。在这种情况下,它假设recipe 对象具有id 属性,并且此ID 是传递给回调getIngredients 并保存到名为recipeId 的局部变量。
  • @Alanaj 很抱歉造成混淆。是的,任何在您的数据集中唯一的配方标识符。通常它是一个 id 字段,但它实际上可以是任何东西,它只需要是唯一的。
  • @Alanaj 这是一个curried 函数,即特别是一个接受 id 并返回一个接受事件对象的函数的函数。这就是写onClick={getIngredients(id)}onClick={e =&gt; getIngredients(e, id)}的区别。
猜你喜欢
  • 2014-07-12
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
  • 2013-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多