【问题标题】:How to use the useState hook to count frequencies of items in an array(React useState hook)?如何使用 useState 钩子计算数组中项目的频率(React useState 钩子)?
【发布时间】:2021-04-04 04:02:29
【问题描述】:

我正在尝试解决这个数组包含 10 个对象的练习,例如

arr = [{txt: "txt1", color: "red"},

{txt: "txt2", 颜色: "green"}, {txt: "txt3", 颜色: "blue"},...]

当我点击一个按钮时,它应该显示一种我知道该怎么做的随机颜色,但我不确定如何同时显示每种颜色出现的次数。

我的功能设置如下:

const countColors = (props) => {
   

const arr = [{txt: "txt1", color: "red"},
{txt: "txt2", color: "green"}, {txt: "txt3", color: "blue"},...]

const [count, setCount] = useState(0);

const choice = () => {
    const randIdx = Math.floor(Math.random() * arr.length);
    return arr[randIdx];
};

const updateScore = () => {
        let randColor = choice(props.arr).color;
        if (randColor === 'red') {
            return setCount((count) => count + 1)
        } else if (randColor === 'green') {
            return setCount((count) => count + 1);
        } else {
            return setCount((count) => count + 1);
        }
    };
const clickHandler = () => {
setCount(updateScore);
}

return (
    <ul>
        <li>Red Counts: {updateScore}</li>
        <li>Green Counts: {updateScore}. 
        </li>
        <li>Blue Counts: {updateScore}</li>
    </ul> 
    <button onClick={clickHandler}>Click</button>

 )
};

当我使用 react 开发工具检查状态时,我不断收到“NaN”或“未定义”。

我想知道是否需要设置 3 个状态来计算每种颜色。

【问题讨论】:

    标签: reactjs react-hooks use-state


    【解决方案1】:

    这是你需要做的:

    const[redCount,setRedCount]=useState(0);
    //similarily for other colors
    const clickHandler = () => {
    let randColor = choice(props.arr).color;
    if (randColor === 'red') {
                setRedCount(redCount+1);
        //similairly do it for other colors
    
    }
    
    return (
        <ul>
            <li>Red Counts: {redCount}</li>
            //similarily for other colors
        </ul> 
        <button onClick={clickHandler}>Click</button>
    
     )
    

    【讨论】:

    • 成功了。谢谢!我做了 3 个 if 语句而不使用 else 或 else if 语句。你觉得我也可以用 switch case 写出这 3 个并行的 if 语句吗?
    【解决方案2】:

    只是为了仔细检查我是否理解正确:您有一组颜色和文本。您想单击一个按钮,该按钮将选择一种随机颜色并告诉您该颜色出现了多少次。假设这是正确的,您的代码中突出的一件事是以下块:

    const [count, setCount] = useState(0);
    ....
    
    const updateScore = () => {
            let randColor = choice(props.arr).color;
            if (randColor === 'red') {
                return setCount((count) => count + 1)
            } else if (randColor === 'green') {
                return setCount((count) => count + 1);
            } else {
                return setCount((count) => count + 1);
            }
        };
    

    首先,请注意您如何始终使用相同的状态,count。假设这被正确使用,您将始终覆盖该状态,这意味着每次您开始计算新颜色时,它都会重新开始。

    其次,您使用setCount 的方式。 state setter 函数不期望一个函数作为参数,而是你想要的实际值。要解决这个初始问题,正确的计数集应该如下:

    setCount(count + 1)

    但是,这就是我要发挥的第一点:每次您尝试设置新计数时,它都会重置另一种颜色的计数。所以这也不好。

    现在,您有两个选择:您可以按照您的建议为每种颜色添加一个单独的 setXCount(setRedCount、setBlueCount 等),或者您可以将它们全部添加到一个对象中,这样您就拥有了一切在单一状态下,每次有新颜色时都不需要添加新颜色。像这样的:

        const [countPerColor, setCountPerColor] = React.useState({});
        ...
        const updateScore = () => {
    
         const randColor = choice(arr).color;
    
            if (countPerColor[randColor]){
              countPerColor[randColor] ++
            } else {
              countPerColor[randColor] = 1
            }
    
            setCountPerColor({...countPerColor})
         };
    
    

    显然,您的退货声明需要相应更新:

    ...
            <li>Red Counts: {countPerColor.red || 0}</li>
            <li>Green Counts: {countPerColor.green || 0}. 
            </li>
            <li>Blue Counts: {countPerColor.blue || 0}</li>
    

    现在总结一下,这是该应用程序的功能演示。

    const App = (props) => {
        const arr = [
            {txt: "txt1", color: "red"},
            {txt: "txt2", color: "blue"},
            {txt: "txt3", color: "blue"},
            {txt: "txt4", color: "red"},
            {txt: "txt5", color: "green"},
            {txt: "txt6", color: "red"},
            {txt: "txt7", color: "green"},
            {txt: "txt8", color: "green"},
            {txt: "txt9", color: "red"},
            {txt: "txt10", color: "blue"},
            ]
        
      const [countPerColor, setCountPerColor] = React.useState({});
    
      const choice = () => {
          const randIdx = Math.floor(Math.random() * arr.length);
          return arr[randIdx];
      };
    
      const updateScore = () => {
    
         let randColor = choice(arr).color;
    
        if (countPerColor[randColor]){
          countPerColor[randColor] ++
        } else {
          console.warn('exists', randColor)
          countPerColor[randColor] = 1
        }
        setCountPerColor({...countPerColor})
          };
    
      return (
      <div>
      <ul>
        <li>Red Counts: {countPerColor.red || 0}</li>
        <li>Green Counts: {countPerColor.green || 0}. 
        </li>
        <li>Blue Counts: {countPerColor.blue || 0}</li>
    </ul> 
      <button onClick={() => updateScore()}>Click me</button>
      </div>
      )
    }
    
    
    ReactDOM.render(
        <App />,
        document.getElementById('app')
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
    <div id="app"></div>

    【讨论】:

    • 非常感谢您的详细解释和演示代码。我了解到我的 setCount 需要一个数字而不是一个函数。我将它与 onClick 处理程序混淆了,在该处理程序中,我经常需要传入箭头函数以使其工作。我喜欢你的方法,因为它是解决这个问题最有效的方法,而且它的可重用性很高。
    • 我没有从您的 updateScore 函数中得到的唯一两件事是设置为 1 的 else 语句和它使用吊具的最后一部分。我认为扩展器用于更新对象中所有颜色的所有计数。
    【解决方案3】:
    import React, { useState } from "react";
    
    const CountColors = (props) => {
        const arr = [
            { txt: "txt1", color: "red" },
            { txt: "txt2", color: "green" },
            { txt: "txt3", color: "blue" },
        ];
    
        const colors = {};
        arr.forEach((item) => {
            colors[item.color] = 0;
        });
        const [count, setCount] = useState(colors);
    
        const choice = () => {
            const randIdx = Math.floor(Math.random() * arr.length);
            return arr[randIdx];
        };
    
        const clickHandler = () => {
            let randColor = choice(props.arr).color;
            console.log(randColor);
            setCount((prevCount) => ({
                ...prevCount, // to presist the old data in the state (spread operator)
                [randColor]: prevCount[randColor] + 1, 
            }));
        };
    
        return (
            <>
                <ul>
                    {arr.map((item) => (
                        <li>{item.color} count: {count[item.color]}</li>
                    ))}
                </ul>
                <button onClick={clickHandler}>Click</button>
            </>
        );
    };
    
    export default CountColors;
    

    试试这个代码,它应该适用于您要求的场景。

    有两种方法可以解决这个问题

    1. 通过为数组中的每种颜色创建单独的状态。
    2. 通过创建一个数组并通过将它们的索引映射到 arr 来存储它们的当前值。
    3. 以颜色名称为键,计数为值动态创建对象(推荐)。

    因为在这种情况下使用对象与数组相比,值的访问时间(读取和写入)非常快,尽管我们不知道第一点的索引。

    我在这里所做的是

    1. 首先我创建了一个名为 colors 的空对象,用于循环遍历 arr 数组并将键设置为颜色名称并将默认值设置为 0。

    2. 在每个点击事件上,根据选择,我增加了值,我使用扩展运算符的原因是 useState 中的值是可变的。

    3. 最后,在 return 语句中,我遍历了数组并打印了项目颜色名称以及使代码看起来不错的值。

    我有两件事将来可能会对你有所帮助

    1. 类/函数名称应以大写字母开头。
    2. 总是返回的 HTML 组件应该用空标签或 React.Fragment 包装。

    【讨论】:

    • 非常感谢您提供详细的示例代码、解释和建议。对此,我真的非常感激。您的方法非常有效,因为它不必处理 if 语句。当有许多颜色选项时效果更好。
    猜你喜欢
    • 2023-03-19
    • 2021-05-23
    • 2021-04-04
    • 2021-02-07
    • 1970-01-01
    • 2021-05-25
    • 2020-11-02
    • 1970-01-01
    • 2023-01-04
    相关资源
    最近更新 更多