使用 React 钩子设置状态后如何从组件调用事件?



我有两个input,其值反映了计数器的状态。

第一个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>

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

出于同样的原因,按Esc会模糊input然后再将其值重置为initialValue

我知道setState()类回调已被useEffect()钩子取代,但是:

  1. 如何从useEffect()调用onComplete(e)函数(像任何其他事件一样(?
  2. 如何仅在按下键时执行此操作,而不是在所有其他键上执行此操作?

提前感谢您的帮助,这是一个交互式沙盒。

我建议将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>

相关内容

  • 没有找到相关文章

最新更新