React Function 没有使用更新的数组?

React Function is not using updated array?

我正在学习 React 并正在编写一个 React 应用程序,它可以让您按下具有值(1、0、-1)的按钮,然后进行一些计算(平均值、%pos 等)。

我编写了一个函数来计算我的 App.js 组件中的平均值

  const [good, setGood] = useState(0)
  const [neutral, setNeutral] = useState(0)
  const [bad, setBad] = useState(0)
  const [allNum, setAll] = useState([])
  const [average, setAverage] = useState(0)

const calcAverage = () => {
  console.log('values in calcAvg for allNum: ', allNum)
  let total = 0;
  for(let i = 0; i < allNum.length; i++) {
    total += allNum[i];
  }
  return total/allNum.length;
}

const handleGoodClick = () => {
  setGood(good + 1)
  setAll(allNum.concat(1))
  setAverage(calcAverage());
}
const handleNeutralClick = () => {
...
}
const handleBadClick = () => {
...
}


  return(
    <div>
     <h1>Give Feedback</h1>
     <Button handleClick={handleGoodClick} text="good"/>
     ...

     <Statistics good={good} neutral={neutral} bad={bad} allNum={allNum} average={average}/>
    </div>
  )
}

统计组件如下:

const Statistics = (props) => {
  console.log('props value is: ', props)
  return(
    <div>
      <h1>Statistics</h1>
      ...
      <Statistic text="Average" value={props.average}/>
    </div>
  )
}

当我按下一个按钮并且应用程序尝试计算平均值时,平均值数组总是落后 1 个值。

即,打开应用程序,按好,Average 显示为 NaN,console.log 显示包含 1 的 allNum 数组,但是当计算 Average 时,allNum 数组中尚未包含 1。为什么不按顺序进行?我怎样才能让它按顺序执行?最佳实践方法是什么?

谢谢

计算收到以前的值的原因是因为 useState 提供的 setState 函数没有同步设置状态。

为了克服这个问题,您可以使用类似 useEffect 的方法在 allNum 发生变化时更新平均值(参考比较)。

const MiscComponent = () => {
    const [good, setGood] = useState(0)
    const [neutral, setNeutral] = useState(0)
    const [bad, setBad] = useState(0)
    const [allNum, setAll] = useState([])
    const [average, setAverage] = useState(0)

    // The useEffect will trigger based on what is entered into the 
    // dependency array: [setAverage, allNum]
    // Each render cycle the values will be compared (reference comparison)
    // to the previous values in the dependency array and if there is a
    // change the effect will be run.
    // NOTE: There is always considered to be a "change" on component mount.

    // setAverage is guarenteed to never change reference once created by useState.
    // Someone else probably has the link to the React docs for this statement.

    // So everytime allNum is updated this effect should run.
    useEffect(() => {
        const average = allNum.reduce((a, b) => a + b, 0) / allNum.length;

        setAverage(average)
    }, [setAverage, allNum])

    // Since the average update will be handled by the useEffect it can
    // now be removed from the click handler.
    const handleGoodClick = () => {
        setGood(good + 1)
        setAll(allNum.concat(1))
    }

    const handleNeutralClick = () => {
        /* Code here */
    }

    const handleBadClick = () => {
        /* Code here */
    }


    return(
        <div>
            <h1>Give Feedback</h1>
            <Button handleClick={handleGoodClick} text="good"/>
            {/* Components Here */}

            <Statistics good={good} neutral={neutral} bad={bad} allNum={allNum} average={average}/>
        </div>
    )
}

另一种选择,如果平均值只受其他状态变量影响(影响?无论如何),您可以使用 useMemo 代替。

const MiscComponent = () => {
    const [good, setGood] = useState(0)
    const [neutral, setNeutral] = useState(0)
    const [bad, setBad] = useState(0)
    const [allNum, setAll] = useState([])

    // useMemo will return a memoized value for average that will only be recalculated
    // based on its associated dependency array.
    const average = useMemo(() => {
        const average = allNum.reduce((a, b) => a + b, 0) / allNum.length;

        return average;
    }, [allNum]);

    // Since the average update will be handled by the useMemo it can
    // now be removed from the click handler.
    const handleGoodClick = () => {
        setGood(good + 1)
        setAll(allNum.concat(1))
    }

    const handleNeutralClick = () => {
        /* Code here */
    }

    const handleBadClick = () => {
        /* Code here */
    }


    return(
        <div>
            <h1>Give Feedback</h1>
            <Button handleClick={handleGoodClick} text="good"/>
            {/* Components Here */}

            <Statistics good={good} neutral={neutral} bad={bad} allNum={allNum} average={average}/>
        </div>
    )
}