繁体   English   中英

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

[英]Consequences of using computed variables vs useState/useEffect

如果我有一个变量的值可以完全基于另一个属性的值导出,那么初始化计算变量与使用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 似乎通过计算属性有这个选项。

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

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

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

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

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

  ...
}

一些附加说明:

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

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

让我们一步一步分析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. 在这里,您正在从paymentAmounts初始化average状态变量。 在初始渲染期间,将根据它计算average状态变量的初始值。 到目前为止,一切都很好。
  2. useEffect中,您正在从 prop 添加对paymentAmounts数组的依赖项。 当一个新的数组实例作为道具传递时,效果回调将运行,并且在初始渲染时也会运行。 所以这里有一个问题,当您在现有数组引用中推送新数字时,效果回调不会运行,它只会在您在具有更新数字的道具中传递新数组实例时运行。 因此,如果您只是在Option1的父组件中执行paymentAmounts.push(400) ,您可能会看到average没有变化。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM