React 函数组件不会更新视图 onClick

React Function Component doesn't update view onClick

我是 React 新手。我有一个我创建的评级组件。我遇到的问题是 onClick 我可以看到评级在控制台中更新,但在页面上我没有看到发生变化。我的理解是 React 组件需要是纯函数,并且最好选择函数式组件而不是 类。当我尝试使用 useState 挂钩时,我的评分被添加到之前的状态并且没有在页面上正确更新。

我的第一个问题是 best/preferred 在组件内更新状态的方法是什么? 如果我使用的是功能组件,是否必须使用挂钩?

请看下面我的代码

export default function Rating(props) {
    let stars = [];
    // const [stars, setStars] = useState([]);
    let star = '☆';

    for (let i = 0; i < props.stars; i++) {
        stars.push(star);
    }

    function rate(index) {
        let filledStar = '★'
        let stars = [];
        // setStars([])


        for (let i = 0; i < props.stars; i++) {
            stars.push(star);
        }
        stars.forEach((star, i) => {
            while (index >= 0) {
                stars[index] = filledStar;
                index--;
            }

        })
        console.log('stars filled', stars)
        return stars

     }

    return (
        <>
        <div className="stars">
            {stars.map((star, i) => (
                <h2 
                    key={i}
                    onClick={() => rate(i)}>{star}
                </h2>
            ))}
        </div>

        </>

    )

}

如果我点击第四颗星,这会按预期返回,但 UI 不会更新。

正如您所建议的,您将需要使用挂钩来寻找您正在寻找的功能(或使用 class 组件)。在您当前的实现中,您实际上并没有使用 React 状态,因此 React 不知道您希望它重新渲染您的组件。此外,您在 rate() 中使用的 stars 数组与组件中的 stars 数组不同。您似乎没有使用 rate()

创建和返回的星数组

您究竟是如何尝试使用 useState 的?

这是一个工作版本,它使用 useStateuseEffect.

填充星星,包括点击的星星

const { Fragment, useEffect, useState } = React;

function Rating(props) {

  // Set state as an empty array
  const [stars, setStars] = useState([]);

  // Called by useEffect his function
  // creates a new array of empty stars
  function initStars() {
    const stars = [];
    for (let i = 0; i < +props.stars; i++) {
      stars.push('☆');
    }
    setStars(stars);
  }

  // Called when the component
  // first mounts, and called only once
  useEffect(() => {
    initStars();
  }, []);

  function rate(e) {

    // Get the id from the star dataset
    const { id } = e.target.dataset;

    // Create a fresh array
    const stars = [];

    // Loop; if the current index is less or
    // equal to the id set it as a filled star
    // otherwise set it to an empty star
    for (let i = 0; i < +props.stars; i++) {
      stars.push(i <= +id ? '★' : '☆');
    }

    // Set the state with the new array
    setStars(stars);

  }

  return (
    <div>
      {stars.map((star, i) => (
        <span
          className="star"
          key={i}
          data-id={i}
          onClick={rate}
        >{star}
        </span>
      ))}
    </div>
  );

};

ReactDOM.render(
  <Rating stars="10" />,
  document.getElementById('react')
);
.star { font-size: 1.8em; }
.star:hover { cursor: pointer; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

所以,看来我需要使用useState hook。我敢肯定还有另一种方法可以做到这一点,但这对我有用。

 export default function Rating(props) {
        const [stars, setStars] = useState([]);
        let star = '☆';

        function initStars() {
            const stars = [];
            for (let i = 0; i < props.stars; i++) {
               stars.push(star);
            }
            setStars(stars);
        }

        // Called when the component
        // first mounts, and called only once
        useEffect(() => {
            initStars();
        }, []);

        function rate(index) {
            console.log('index', index);
            let filledStar = '★'
            let stars = [];

            console.log('setStars', stars);

            for (let i = 0; i < props.stars; i++) {
               stars.push(star);
            }
            stars.forEach((star, i) => {
                while (index >= 0) {
                   stars[index] = filledStar;
                   index--;
                }

            })
            console.log('stars filled', stars)
            // return stars
            setStars(stars)

         }

         // console.log('stars', stars);

        return (
        <>
            <div className="stars">
                {stars.map((star, i) => (
                    <h2 
                        key={i}
                        onClick={() => rate(i)}>
                        {star}
                    </h2>
                ))}
            </div>

        </>

    )

}