使用计算变量与 useState/useEffect 的结果

时间:2021-06-28 15:33:01

标签: javascript reactjs use-effect use-state

如果我有一个变量的值可以完全基于另一个属性的值派生,那么初始化计算变量与使用 useState/useEffect 的组合相比是否有任何后果/陷阱?跟踪变量?让我用一个人为的例子来说明:

/**
 * ex paymentAmounts: [100, 300, 400]
 */
const Option1 = ({paymentAmounts}) => {
  const [average, setAverage] = useState(paymentAmounts.reduce((acc, curr) => curr + acc, 0) / paymentAmounts.length)

  useEffect(() => {
    setAverage(paymentAmounts.reduce((acc, curr) => curr + acc, 0) / paymentAmounts.length)
  }, [paymentAmounts])

  return (
    <div>
      Average: {average}
    </div>
  )
}

或者更简单

/**
 * ex paymentAmounts: [100, 300, 400]
 */
const Option2 = ({paymentAmounts}) => {
  const average = paymentAmounts.reduce((acc, curr) => curr + acc, 0) / paymentAmounts.length

  return (
    <div>
      Average: {average}
    </div>
  )
}

我是否会因为使用 Option2 而放弃任何控制和/或 React 的好处?

Vue.js 似乎通过 computed properties 提供了这个选项。

3 个答案:

答案 0 :(得分:2)

使用钩子或其他状态跟踪功能的唯一原因是您是否计划更改组件本身的状态。从你给出的例子来看,情况并非如此。如果 prop paymentAmounts 被更新,组件将被 React 更新,你的计算常量 average 也会更新。

因此,在这里使用 useState 和 useEffect 没有任何好处。使用计算常数保持简单!

答案 1 :(得分:1)

当您想要在组件跟踪某些内容(或在其子组件之间共享)重新渲染之间时,您可以使用状态(如在 useState 中那样) .在您的情况下,您从父组件(父组件是实际的状态持有者)获取此“状态”。父状态(在您的情况中为 paymentAmounts)的每个更改都会自动反映在您的子组件中。

作为一般“规则”,不要将状态用于可计算的数据。请记住,状态跟踪变量的每次更改都会强制组件重新渲染。另一个不好的用法示例是:

const Example = ({variable1}) => {
  const [variable, setVariable] = useState(variable1);

  ...
}

补充说明:

  1. 在您的第一个解决方案中,您通过使用引入了额外的开销 useEffect。正如已经指出的,您的子组件将 总是重新渲染并重新计算 average 状态变化。
  2. 您在 setAverage 钩子中使用了错误的 useEffect。二传手 采用新值或接受当前值的函数 并返回新的。
  3. 不确定您对 paymentAmounts / paymentAmounts.length 计算的期望。我想这只是一个假人 代码,但如果没有,请查看它。您将数组本身(而不是其值的总和)除以其长度。

所以,简而言之 - 是的,您应该使用一个简单的变量来计算平均值并丢弃 useState/useEffect。您不仅没有放弃任何好处,而且实际上使您的代码更加高效、可维护、可读和无错误。

答案 2 :(得分:1)

让我们逐步分析 Option1 组件:

/**
 * ex paymentAmounts: [100, 300, 400]
 */
const Option1 = ({paymentAmounts}) => {

  //1
  const [average, setAverage] = useState(paymentAmounts / paymentAmounts.length)

  //2
  useEffect(() => {
   setAverage(paymentAmounts.reduce((acc, curr) => curr + acc, 0) / paymentAmounts.length)
  }, [ paymentAmounts])

  return (
    <div>
      Average: {average}
    </div>
  )
}
  1. 在这里,您正在从 average 道具初始化 paymentAmounts 状态变量。在初始渲染期间,average 状态变量的初始值将由此计算。到目前为止一切顺利。
  2. useEffect 中,您正在从 prop 添加对 paymentAmounts 数组的依赖。当一个新的数组实例作为 prop 传递时,效果回调将在初始渲染时运行。所以这里有一个问题,当您在现有数组引用中推送新数字时,效果回调不会运行,只有当您在具有更新数字的 prop 中传递新数组实例时,它才会运行。 因此,如果您只是在 average 的父组件中执行 paymentAmounts.push(400),您可能看不到 Option1 的变化。

因此对于所有用例来说,Option2 是实现您想要做的事情的最佳方式。无需在此功能组件中引入状态。 UI 中的值可以从 props 本身派生。