【问题标题】:React Button Click Event Update State Value But Not Reflected on Button Text反应按钮单击事件更新状态值但不反映在按钮文本上
【发布时间】:2020-04-23 06:59:24
【问题描述】:

仍在学习 React 的一些技巧。

我在呈现按钮列表的地方有以下代码:

import React, { useState, useEffect } from 'react';
const MyComponent = (props) => {
  const [buttonList, setButtonList] = useState([]);
  useEffect(() => { getButtonList()}, [])

  const getButtonList = () => {
    let data = [
      {id: 1, name: 'One', selected: false },
      {id: 2, name: 'Two', selected: false },
      {id: 3, name: 'Three', selected: false }
    ]

    setButtonList(data)
  }

  const ButtonItem = ({ item }) => {
    const btnClick = (event) => {
      const id = event.target.value

      buttonList.forEach((el) => {
        el.isSelected = (el.id == id) ? true : false
      })

      setButtonList(buttonList)
      console.log('buttonList', buttonList)
    }

    return (
      <button type="button" 
       className={ "btn mx-2 " + (item.isSelected ? 'btn-primary' : 'btn-outline-primary') }
       onClick={btnClick} value={item.id}>
       {item.name + ' ' + item.isSelected}
      </button>
    )
  }

  return (
    <div className="container-fluid">
      <div className="card mb-3 rounded-lg">
        <div className="card-body">
          {
            buttonList.map(item => (
              <ButtonItem key={item.id} item={item} />
             ))
           }
         </div>
      </div>
    </div>
  )
}

export default MyComponent;

所以按钮呈现:

[ One false ] [ Two false ] [ Three false ]

当我单击任何按钮时,我可以在 Chrome React 工具上看到该按钮的 isSelected 的值变为 true。我还可以确认在状态的开发工具中单击的按钮的特定数组项(在钩子下),值为true

点击按钮的文本不显示[ One true ] 如果我点击按钮一。我在这里错过了什么?

附:请注意,我还想更改按钮的类,但我认为如果我让按钮 isSelected 值在整个组件中是已知的,那部分将得到解决。

代码演示: https://codesandbox.io/s/laughing-keller-o5mds?file=/src/App.js:666-735

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    问题:您正在改变状态对象,而不是返回新的状态引用。您之前也使用=== 将字符串id 与数字id 进行比较,所有比较都返回false。

    解决方案:使用函数更新和array.map通过返回一个新数组来更新状态。

    const ButtonItem = ({ item }) => {
      const btnClick = event => {
        const id = event.target.value;
    
        setButtonList(buttons =>
          buttons.map(button => ({
            ...button,
            isSelected: button.id == id
          }))
        );
      };
    
      ...
    };
    

    建议:分解出btnClick 处理程序,它只需要定义一次。将itemid 属性库化,这样您就可以使用===

    const btnClick = id => event => {
      setButtonList(buttons =>
        buttons.map(button => ({
          ...button,
          isSelected: button.id === id
        }))
      );
    };
    

    更新点击处理程序的附加以传递项目ID

    const ButtonItem = ({ item }) => {
      return (
        <button
          type="button"
          className={
            "btn mx-2 " +
            (item.isSelected ? "btn-primary" : "btn-outline-primary")
          }
          onClick={btnClick(item.id)} // <-- pass item.id to handler
          value={item.id}
        >
          {item.name + " " + item.isSelected}
        </button>
      );
    };
    

    【讨论】:

    • 我可以看到您提供的示例 CodeSandbox 有效,但我想了解为什么我将值直接设置为数组项的部分不起作用?此外,我的 id 比较使用的是 ==,因为我知道我的代码类型不同,但我正在更改代码以使用字符串 GUID 作为 ID。
    • @Chett 它通常不起作用,因为您将相同的数组引用保存回状态。 React 使用浅层引用相等检查来确定组件何时应该重新渲染。
    • 我最初也尝试排除 btnClick 函数(如您在示例中设置的那样),但我遇到了一些奇怪的错误,不允许我传递参数。因此,我必须将其更改为我的代码示例中的内容。我有一个附加问题。这条线const btnClick = id =&gt; event =&gt; 对我来说很奇怪。通常我写像const fn = (param) =&gt; {}这样的函数。我在哪里学习这些东西?
    • @Chett 是一个叫做Currying的概念。 const fn = (a, b) =&gt; {} 附加为onClick={b =&gt; fn(a, b)} 等效于const fn =&gt; a =&gt; b =&gt; {} 附加为onClick={b =&gt; fn(a)(b)} 或更直接为onClick={fn(a)}。它允许您将a 存储在回调实例的外壳中。 f(a) = b =&gt; {}
    【解决方案2】:

    在您的 btnClick 处理程序中,您正在改变您的状态,您应该创建一个新值并改为分配它:

    import React from "react";
    import "./styles.css";
    import { useState, useEffect } from "react";
    
    const ButtonItem = ({ item, onClick }) => {
      return (
        <button
          type="button"
          className={
            "btn mx-2 " + (item.isSelected ? "btn-primary" : "btn-outline-primary")
          }
          onClick={() => onClick(item.id)}
          value={item.id}
        >
          {item.name + " " + item.isSelected}
        </button>
      );
    };
    
    const MyComponent = props => {
      const [buttonList, setButtonList] = useState([]);
      useEffect(() => {
        getButtonList();
      }, []);
    
      const getButtonList = () => {
        let data = [
          { id: 1, name: "One", isSelected: false },
          { id: 2, name: "Two", isSelected: false },
          { id: 3, name: "Three", isSelected: false }
        ];
    
        setButtonList(data);
      };
    
      const btnClick = id => {
        const updatedList = buttonList.map(el => ({
          ...el,
          isSelected: el.id === id
        }));
    
        setButtonList(updatedList);
        console.log("buttonList", updatedList);
      };
    
      return (
        <div className="container-fluid">
          <div className="card mb-3 rounded-lg">
            <div className="card-body">
              {buttonList.map(item => (
                <ButtonItem key={item.id} item={item} onClick={btnClick} />
              ))}
            </div>
          </div>
        </div>
      );
    };
    
    export default MyComponent;
    

    【讨论】:

    • 感谢您的回答。我试过你的解决方案,它也有效。虽然我必须标记最先得到的答案(Drew Reese)并帮助解决问题+解释一些术语。我也很感激你的时间。
    猜你喜欢
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多