【问题标题】:React Function Component doesn't update view onClickReact 函数组件不更新视图 onClick
【发布时间】:2022-01-05 20:54:52
【问题描述】:

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

我的第一个问题是在组件中更新状态的最佳/首选方式是什么? 如果我使用的是功能组件,我是否必须使用钩子?

请看下面我的代码

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 不会更新。

【问题讨论】:

    标签: javascript reactjs react-hooks jsx


    【解决方案1】:

    这是一个工作版本,它使用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>

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 如果您查看我的代码,我已经注释掉了 useState 和 setStars。我用它们代替了 stars 变量。但是,当我在它的位置使用它时,它会将先前的状态添加到我在控制台和 UI 中更改的状态,它只会使我设置的星星数量增加一倍,然后不再更改。
      • 另外,我们有意不在 rate 函数中使用相同的 stars 数组,因为每次用户设置评分时我都想重置数组。
      • 您是否打算点击一个星号并填写该星号及其前面的星号?
      • 是的。因此,如果我单击第四颗星(第三个索引),它应该填写该星和后续星。我的逻辑是正确的,但是组件没有更新,我不确定最好的方法是什么。
      【解决方案3】:

      所以,看来我需要使用 useState 挂钩。我确信还有另一种方法可以做到这一点,但这对我有用。

       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>
      
              </>
      
          )
      
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-26
        • 1970-01-01
        • 2021-07-15
        • 1970-01-01
        • 2019-05-12
        • 2019-03-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多