javascript - 使用 React hooks 设置状态后如何从组件调用事件?

标签 javascript reactjs react-hooks keyboard-events use-effect

我有两个input其值反射(reflect)计数器的状态。

第一个input当其值发生变化时立即设置状态。这个input可以代表应用程序的其他部分改变状态。

第二个input我制作的名为 LazyInput 的组件的一部分。 <强> LazyInput不会立即更新状态,仅在模糊时才更新,我认为这提供了更好的可用性。此输入可以表示用户输入,例如要设置的计时器或屏幕上某些内容的位置。

但是,我想要 LazyInput不要表现得“懒惰”,并且实际上立即更新计数器仅当通过 键更改值时

App代码(包括正常的 input )如下:

const {Fragment, useState, useEffect} = React;

function LazyInput({ name, value, onComplete }) {
  const initialValue = value;
  const [lazyValue, setLazyValue] = useState(value);

  // Sync to state changed by the regular input
  useEffect(() => {
    setLazyValue(value);
  }, [value]);

  const handleKeyDown = e => {
    const { key, target } = e;
    switch (key) {
      case "ArrowUp": {
        setLazyValue(parseFloat(lazyValue) + 1);
        onComplete(e);
        break;
      }
      case "ArrowDown": {
        setLazyValue(parseFloat(lazyValue) - 1);
        onComplete(e);
        break;
      }
      case "Escape": {
        setLazyValue(initialValue);
        target.blur();
        break;
      }
      case "Enter": {
        target.blur();
        break;
      }
      default:
        return;
    }
  };
  return (
    <input
      name={name}
      value={lazyValue}
      onChange={e => setLazyValue(e.target.value)}
      onKeyDown={e => handleKeyDown(e)}
      onBlur={e => onComplete(e)}
    />
  );
}

function App() {
  const [counter, setCounter] = useState(0);

  function handleCounterChange(e) {
    setCounter(parseFloat(e.target.value));
  }

  return (
    <Fragment>
      <div>Counter: {counter}</div>
      <LazyInput
        name="counter"
        value={counter}
        onComplete={e => handleCounterChange(e)}
      />
      <input
        value={counter}
        type="number"
        onChange={e => handleCounterChange(e)}
      />
    </Fragment>
  );
}

ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="app"></div>

问题是:按 键会触发 onComplete(e) setLazyValue()之前函数– 我想是因为它是异步的 – 与状态失去同步。

出于同样的原因,按 Esc 会模糊 input 将其值重置为 initialValue 之前.

我知道setState()类回调has been substituted by the useEffect() hook ,但是:

  1. 如何调用 onComplete(e)功能 – 与任何其他事件一样 – 来自 useEffect()
  2. 如何仅在按下 键而不是所有其他键时执行此操作?

预先感谢您的帮助,这是 interactive sandbox .

最佳答案

我建议将 countersetCounter 传递给 LazyInput,以便可以直接访问它们。 useEffect 可以被赋予一个依赖项数组,只有当它的依赖项之一发生变化时它才会触发。但是,useEffect 不适合这种情况。您可以将 counter 指定为 useEffect 的依赖项,但每次 counter 的值发生更改时它都会触发。 useEffect 只能监视变量,不能向其传递值。我建议阅读这篇文章,了解 useEffect 的工作原理以及何时使用它。

https://overreacted.io/a-complete-guide-to-useeffect/

我的解决方案将 countersetCounter 传递给 LazyInput。如果您希望 LazyInput 保留其自己的状态,可以使用 useEffect 来监视 lazyValue。当lazyValue改变时,counter应该更新。

这是我的解决方案:

const {Fragment, useState, useEffect} = React;

function LazyInput({ name, counter, setCounter }) {
  const [initialValue] = useState(counter);

  const handleKeyDown = e => {
    const { key, target } = e;

    switch (key) {
      case "ArrowUp": {
        setCounter(counter + 1);
        break;
      }
      case "ArrowDown": {
        setCounter(counter - 1);
        break;
      }
      case "Escape": {
        setCounter(initialValue);
        target.blur();
        break;
      }
      case "Enter": {
        target.blur();
        break;
      }
      default:
        return;
    }
  };
  return (
    <input
      name={name}
      value={counter}
      onChange={e => setCounter(e.target.value)}
      onKeyDown={e => handleKeyDown(e)}
    />
  );
}

function App() {
  const [counter, setCounter] = useState(0);

  return (
    <Fragment>
      <div>Counter: {counter}</div>
      <LazyInput name="counter" counter={counter} setCounter={setCounter} />
      <input
        value={counter}
        type="number"
        onChange={e => setCounter(parseFloat(e.target.value))}
      />
    </Fragment>
  );
}

ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>


<div id="app"></div>

关于javascript - 使用 React hooks 设置状态后如何从组件调用事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60119101/

相关文章:

reactjs - 为什么我在 "Cannot find file ' 中收到此错误 : './src' ./App'。":?

css - 如何从 webpack 生成的 NPM 包中正确加载资源?

javascript - 在 React 中切换暗模式和亮模式

reactjs - React Onclick - 没有重载匹配这个调用

reactjs - 使用reactjs中的useState钩子(Hook)更新对象数组

javascript - 使用指令时 Angular 重新加载 Controller

javascript - 拉斐尔 JS getbyid 不起作用

javascript - 来自外部文件的流类型导入类型 - 名称已绑定(bind)

javascript - vue-uuid 的 v-for 键的 uuid?

javascript - Angular 无法找到模块,直到重建