React hooks 传值问题 || defaultValue 和更新计数器

React hooks problem with passing value || defaultValue and updating counter

在下面找到示例代码和屏幕截图。

两个问题:

  1. counter 没有正确更新,是否允许写类似 setCounter((prev) => { return { counter: prev.counter - value || 1 }}); 的内容?

  2. setCounter(counter + value || 1)正确的做法是什么 要工作,在缺少实际值的情况下使用 1?

在下面找到示例代码和屏幕截图。

const Counter = (props) => {
  const [counter, setCounter] = useState(0);

  const handleCounterChange = (action, value) => {
    switch (action) {
      case "+":
        setCounter(counter + value || 1);
        break;
      case "-":
        setCounter(counter - value || 1);
        break;
      default:
        setCounter(counter + value || 1);
        break;
    }
  };

  return (
    <div className={classes.Counter}>
      <CounterOutput value={counter} />
      <CounterControl
        label="+"
        clicked={() => handleCounterChange("+")}
      />
      <CounterControl
        label="-"
        clicked={() => handleCounterChange("-")}
      />
      <CounterControl
        label="+ 5"
        clicked={() => handleCounterChange("+", 5)}
      />
      <CounterControl
        label="- 5"
        clicked={() => handleCounterChange("-", 5)}
      />
    </div>
  );
};

export default Counter;

setCounter(counter + value || 1); 的问题是 JS 将尝试首先计算 counter + value 然后 布尔测试。因此,例如,如果您的 counter 值为 1 并且您减去 1 的值,结果为 0,这是错误的,那么 1 将保存为 counter 的新值。这可能不是你想要的。

console.log(100 + undefined || 1); // expect 101 but result is 1
console.log(1 + -1 || 1); // expect 0 but result is 1

不过您可以对优先级进行分组,即 counter + (value || 1)

console.log(100 + (undefined || 1)); // 101

如果您只是想为 value 提供默认值 1,那么您可以在签名中这样做。如果 value 未定义,它将被分配 1 的值。并使用功能状态更新。

const handleCounterChange = (action, value = 1) => {
  switch (action) {
    case "+":
      setCounter(counter => counter + value);
      break;
    case "-":
      setCounter(counter => counter - value);
      break;
    default:
      setCounter(counter => counter + value);
      break;
  }
};

关于此“reducer”类型模式的旁注是 return 如果操作不是您专门处理的操作,则现有状态。

const handleCounterChange = (action, value = 1) => {
  switch (action) {
    case "+":
      setCounter(counter => counter + value);
      break;
    case "-":
      setCounter(counter => counter - value);
      break;
    default:
      // ignore, i.e. don't update state at all
      break;
  }
};

本身不是答案,但不要使用额外的不必要功能使您的代码过于复杂。您的逻辑非常简单,添加额外的功能会使阅读变得更加复杂并添加奇怪的逻辑(默认情况下是什么?)


  return (
    <div className={classes.Counter}>
      <CounterOutput value={counter} />
      <CounterControl
        label="+"
        clicked={() => setCounter(state => state+1)}
      />
      <CounterControl
        label="-"
        clicked={() => setCounter(state => state-1)}
      />
      <CounterControl
        label="+ 5"
        clicked={() => setCounter(state => state+5)}
      />
      <CounterControl
        label="- 5"
        clicked={() => setCounter(state => state-5)}
      />
    </div>
  );

如果你想把它晾干一点,你总是可以这样做:


  const change = by => () => setCounter(count => count+by)

  return (
    <div className={classes.Counter}>
      <CounterOutput value={counter} />
      <CounterControl
        label="+"
        clicked={change(1)}
      />
      <CounterControl
        label="-"
        clicked={change(-1)}
      />
      <CounterControl
        label="+ 5"
        clicked={change(5)}
      />
      <CounterControl
        label="- 5"
        clicked={change(-5)}
      />
    </div>
  );

此处提出了一些出色的重构。我绝对想通读建议并进行重构。

至于不可预测计数器的原因,假设点击参数进入状态更新函数,则存在操作顺序问题。考虑这种情况。

值为 undefined

从上面代码的写法来看,当值为undefined时,我们希望在counter状态下增加一个value数字。由于操作顺序,这不是正在发生的事情。这个错误隐藏在众目睽睽之下,因为通常 valuecounter 都是 1,所以看起来状态没有改变。

> let value = undefined
undefined
> let counter = 1
undefined
> counter + value || 100
100

最后一条语句说“累加计数器加上 undefined”,这是 NaN,也是假的。这种情况下的结果将始终是 'or' 语句的另一面,100.

使用括号更改操作顺序,并创建所需的行为。

> counter + (value || 100)
101